概述
什么是读写事务呢? 顾名思义,读事务就只会读取表中的数据,而写事务则会对表中的数据进行修改操作,产生redo日志。
当我们执行一条SQL时,它是否会修改数据表中的数据呢?
SELECT * FROM students WHERE sid = 10 for update;
明明是一条只读查询语句,为什么会有修改数据的操作?
下面我们就来看看这个问题。
事务
这就需要从事务来聊起了,事务是数据库操作的最小单元,事务的四大特性是原子性、一致性、隔离性、持久性。
- 原子性
是指事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。 - 一致性
是指事务必须是数据库从一个一致性状态变到另一个一致性状态。 - 隔离性
是指多个事务并发执行时,一个事务的执行不能被其他事务干扰。 - 持久性
是指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。
事务表示
事务的表示有两种方式:
- 事务ID(Transaction ID,XID):事务ID是一个数字,它唯一标识一个事务。
- 事务记录(Transaction Record):事务记录是一组数据,它记录了事务的开始时间、结束时间、事务的状态、事务的操作、事务的结果等信息。
要在多事务并发执行时,保证事务的特性,就要记录事务的开始时间、结束时间,对每行数据修改的开始时间和结束时间。
时间需要非常精确,精确时间的记录是一件非常困难的事情,因此大多数数据库系统采用事务号来代替时间戳来记录事务的开始时间和结束时间。
事务ID
事务ID是PostgreSQL中事务的唯一标识,它是一个连续递增数字,它唯一标识一个事务。
目前PostgreSQL的事务ID是64位的数字,实际起作用的是低32位,因此它需要设计为一个环形计数器,到达最大值后返回到最小值,进行循环利用。
事务状态
事务状态有以下几种:
- 未提交(Not ***mitted):事务还没有被提交。
- 已提交(***mitted):事务已经被提交。
- 中止(Aborted):事务被中止。
- 子事务提交(Subtransaction ***mitted):子事务已经被提交。
在PostgreSql中,事务状态与事务ID一起使用,来表示事务的生命周期,它的事务状态设置与多版本机制和事务记录方式有前,这在另外的文章中会详细介绍。
事务号记录
通过事务的基本概念知道,多事务并发执行时需要并发控制来保证事务的隔离和一致性,在PostgreSql数据库中,为每行数据都增加两个隐藏字段,分别是xmin、xmax;
这两字段分别表示:
- xmin:表示该插入该行数据的事务号;
- xmax:表示更新或删除该行数据的事务号;
事务的产生
一般提到使用事务时,postgreSQL中有两种使用事务的方式:
- 显式事务:通过BEGIN、***MIT、ROLLBACK等命令显式地开启、提交、回滚事务。
- 自动事务:每次输入的SQL语句会自动开启一个事务,如果执行成功,则提交事务,如果执行失败,则回滚事务。
然而,并不是所有事务都要分配事务ID,由于Postgresql的MV***机制,只有涉及到修改数据操作时才会分配事务ID,没有事务ID也就不会更新xmin、xmax字段。
再回到文章开头的问题,因为读操作,但是增加 for update 关键字,对相当于更新数据操作,因此会分配事务ID,会将事务号更新到xmax字段当中,写入数据行中。
总结
读写事务是数据库事务的一种,它是指事务中只读操作和写操作的分离。读写事务的产生是通过记录事务号来实现的,事务号记录了事务的状态,并且记录事务的redo日志。