openGauss源码解析(73)
openGauss源码解析:事务机制源码解析(5)
5.2.3 MVCC可见性判断机制
openGauss利用多版本并发控制来维护数据的一致性。当扫描数据时每个事务看到的只是拿快照那一刻的数据,而不是数据当前的最新状态。这样就可以避免一个事务看到其他并发事务的更新而导致不一致的场景。使用多版本并发控制的主要优点是读取数据的锁请求与写数据的锁请求不冲突,以此来实现读不阻塞写,写也不阻塞读。下面介绍事务的隔离级别以及openGauss可见性判断CSN机制。
1. 事务隔离级别
SQL标准考虑了并行事务间应避免的现象,定义了以下几种隔离级别,如表5-5所示。
表5-5 事务隔离级别
隔离级别 | P0:脏写 | P1:脏读 | P2:不可重复读 | P3:幻读 |
读未提交 | 不可能 | 可能 | 可能 | 可能 |
读已提交 | 不可能 | 不可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 不可能 | 可能 |
可串行化 | 不可能 | 不可能 | 不可能 | 不可能 |
(1) 脏写(dirty write):两个事务分别写入,两个事务分别提交或回滚,则事务的结果无法确定,即一个事务可以回滚另一个事务的提交。
(2) 脏读(dirty read):一个事务可以读取另一个事务未提交的修改数据。
(3) 不可重复读(fuzzy read):一个事务重复读取前面读取过的数据,数据的结果被另外的事务修改。
(4) 幻读(phantom):一个事务重复执行范围查询,返回一组符合条件的数据,每次查询的结果集因为其他事务的修改发生改变(条数)。
在各类数据库实现的过程中,并发事务产生了一些新的现象,在原来的隔离级别的基础上,有了一些扩展。如表5-6所示。
表5-6 事务隔离级别扩展
隔离级别 | P0:脏写 | P1:脏读 | P4:更新丢失 | P2:不可重复读 | P3:幻读 | A5A:读偏斜 | A5B:写偏斜 |
读未提交 | 不可能 | 可能 | 可能 | 可能 | 可能 | 可能 | 可能 |
读已提交 | 不可能 | 不可能 | 可能 | 可能 | 可能 | 可能 | 可能 |
可重复读 | 不可能 | 不可能 | 不可能 | 不可能 | 可能 | 不可能 | 不可能 |
快照一致性读 | 不可能 | 不可能 | 不可能 | 不可能 | 偶尔 | 不可能 | 可能 |
可串行化 | 不可能 | 不可能 | 不可能 | 不可能 | 不可能 | 不可能 | 不可能 |
(5) 更新丢失(lost update):一个事务在读取元组并更新该元组的过程中,有另一个事务修改了该元组的值,导致最终这次修改丢失。
(6) 读偏斜(read skew):假设数据x,y有隐式的约束x+y=100;事务一读取x=50;事务二写x=25并更新y=75保证约束成立,事务二提交,事务一再读取y=75,导致事务一中读取x+y=125,不满足约束。
(7) 写偏斜(write skew):假设数据x,y有隐式的约束x+y<=100;事务一读取x=50,并写入y=50;事务二读取y=30并写入x=70,并提交;事务一再提交;最终导致x=70,y=50不满足x+y<=100的约束。
openGauss提供读已提交隔离级别和可重复读隔离级别:在实现上可重复读隔离级别无幻读问题,有A5B写偏斜问题。
2. CSN机制
1) CSN原理简单如图5-12所示。
图5-12 CSN原理
每个非只读事务在运行过程中会取得一个xid号,在事务提交时会推进CSN,同时会将当前CSN与事务的xid映射关系保存起来(CSNLOG)。图5-12中,实心竖线标识取snapshot(快照)时刻,会获取最新提交CSN(3)的下一个值4。TX1、TX3、TX5已经提交,对应的CSN号分别是1、2、3。TX2、TX4、TX6正在运行,TX7、TX8是未来还未开启的事务。对于当前snapshot而言,严格小于CSN号4的事务提交结果均可见;其余事务提交结果在获取快照时刻还未提交,不可见。
2) MVCC快照可见性判断的流程
获取快照时记录当前活跃的最小的xid,记为snapshot.xmin。当前最新提交的“事务id(latestCompleteXid) + 1”,记为snapshot.xmax。当前最新提交的“CSN号 + 1”(NextCommitSeqNo),记为snapshot.csn。可见性判断的简易流程如图5-13所示。
图5-13 MVCC快照可见性判断流程
(1) xid大于等于snapshot.xmax时,该事务id不可见。
(2) xid比snapshot.xmin小时,说明该事务id在本次事务启动以前已经结束,需要去CLOG查询事务的提交状态,并在元组头上设置相应的标记位。
(3) xid处于snapshot.xmin和snapshot.xmax之间时,需要从CSN-XID映射中读取事务结束的CSN;如果CSN有值且比snapshot.csn小,表示该事务可见,否则不可见。
3) 提交流程
事务提交流程如图5-14所示。
图5-14 提交流程
(1) 设置CSN-XID映射commit-in-progress标记。
(2) 原子更新NextCommitSeqNo值。
(3) 生成redo日志,写CLOG,写CSNLOG。
(4) 更新PGPROC将对应的事务信息从PGPROC中移除,xid设置为InvalidTransactionId、xmin设置为InvalidTransactionId等。
4) 热备支持
在事务的提交流程步骤(1)与(2)之间,增加commit-in-progress的XLOG日志。备机在读快照时,首先获取轻量锁ProcArrayLock,并计算当前快照。如果使用当前快照中的CSN时,碰到xid对应的CSN号有COMMITSEQNO_COMMIT_INPROGRESS标记,则必须等待相应的事务提交XLOG回放结束后再读取相应的CSN判断是否可见。为了实现上述等待操作,备机在对commit-in-progress的XLOG日志做redo操作时,会调用XactLockTableInsert函数获取相应xid的事务排他锁;其他的读事务如果访问到该xid,会等待在此xid的事务锁上直到相应的事务提交XLOG回放结束后再继续运行。