多版本并发控制MVCC

📅 2026/7/18 9:39:14
多版本并发控制MVCC
1. 概念通过保存数据在某个时间点的快照来实现并发控制的。也就是说不管事务执行多长时间事务内部看到的数据是不受其它事务影响的根据事务开始的时间不同每个事务对同一张表同一时刻看到的数据可能是不一样的。与传统锁机制的区别悲观并发控制锁默认认为冲突会发生因此通过加锁来预防。读会阻塞写写会阻塞读。乐观并发控制MVCC默认认为冲突不常发生。写操作会创建数据的新版本而读操作去访问旧版本。因此读操作永远不会阻塞写操作写操作也永远不会阻塞读操作极大提升了并发性能。2.DM物理记录结构为了适应多版本机制高效地获取历史记录每一条物理记录中包含了两个字段TID和 RPTR。TID 保存修改记录的事务号RPTR 保存回滚段中上一个版本回滚记录的物理地址。插入、删除和更新物理记录时。RPTR 指向操作生成的回滚记录的物理地址。物理记录格式如下3.MVCC的好处数据库并发场景有三种分别为读-读不存在任何问题也不需要并发控制读-写有线程安全问题可能会造成事务隔离性问题可能遇到脏读幻读不可重复读写-写有线程安全问题可能会存在更新丢失问题比如第一类更新丢失第二类更新丢失多版本并发控制MVCC是一种用来解决读-写冲突的无锁并发控制也就是为事务分配单向增长的时间戳为每个修改保存一个版本版本与事务时间戳关联读操作只读该事务开始前的数据库的快照。所以MVCC可以为数据库解决以下问题A.在并发读写数据库时可以做到在读操作时不用阻塞写操作写操作也不用阻塞读操作提高了数据库并发读写的性能B.同时还可以解决脏读幻读不可重复读等事务隔离问题但不能解决更新丢失问题。4.MVCC的两种模式达梦数据库为用户提供了两种MVCC模式通过参数TRX_VIEW_MODE控制1.基于回滚记录的MVCCTRX_VIEW_MODE0数据行包含事务TID和版本指针RPTR使得记录间单向连接形成链式版本结构。事务根据当前活动事务的视图依据链式版本结构构成可见的记录集合[]。2.基于时间戳的MVCCTRX_VIEW_MODE1全局维护一个大数组CMTARRCommit Array长度由参数TRX_CMTARR_SIZE控制单位百万。事务在启动时获取一个时间戳SNAP_CMTSEQ提交时将CMTARR中对应位置事务号的值设置为当前时间。数据版本的可见性通过比较SNAP_CMTSEQ和CMTSEQ来判断如果SNAP_CMTSEQ CMTSEQ则数据版本可见否则不可见。基于时间戳的MVCC是达梦数据库当前版本的默认模式它提供了更高的性能和更低的内存开销。5.MVCC的可见性判断规则实现达梦MVCC的关键是可见性判断找到对当前事务可见的特定版本数据。达梦使用以下规则进行可见性判断A.物理记录的TRXID等于当前事务号说明是本事务修改的物理记录物理记录可见。B.物理记录的TRXID不在活动事务表中并且TRXID小于NEXT_TID下一个事务号物理记录可见。C.物理记录的TRXID包含在活动事务表中或者TRXID大于等于NEXT_TID物理记录不可见。如果当前物理记录不可见则按照该记录的ROLLPTR指示找到其上一个版本并再次应用上述规则进行可见性判断直到找到可见版本为止。对于基于时间戳的MVCC模式可见性判断更为简单只需要比较事务的时间戳即可这大大提高了判断效率。6.MVCC与事务隔离级别的集成DM支持三种事务隔离级别读未提交、读提交和串行化。默认是读提交级别。A.读未提交Read Uncommitted在该级别下事务可以读取其他事务尚未提交的数据可能导致脏读问题。多次读取同一数据可能得到不同的结果。B.读已提交Read Committed在该级别下事务只能读取其他事务已经提交的数据。DM在执行每个SQL语句前收集一次活动事务信息因此同一事务中的不同查询可能看到不同的数据状态。C.串行化Serializable在该级别下数据库管理系统会严格控制事务的执行顺序通过加锁来实现。如果一个事务正在修改数据其他试图读取或修改同一数据的事务将被阻塞直到第一个事务完成。在事务启动时收集一次活动事务信息确保同一事务中的所有查询看到一致的数据状态。当使用串行化隔离级别时如果一个串行化事务试图更新或删除数据而这些数据在此事务开始后被其他事务修改并提交时将报串行化事务被打断错误。7.MVCC 的实现原理MVCC的实现依赖于三个核心组件数据的隐藏列 、回滚日志Undo Log 和ReadView读视图。三者协同工作使得事务能够访问到符合其隔离级别的数据版本。7.1数据的隐藏列InnoDB存储引擎会为表中的每一行数据添加三个隐藏列用于记录版本信息和事务相关数据DB_TRX_ID 记录最后一次修改该行数据的事务ID6字节。每次事务对行数据执行INSERT/UPDATE/DELETE操作时都会将当前事务的ID写入该列。DB_ROLL_PTR回滚指针7字节指向该行数据的回滚日志Undo Log通过该指针可以找到数据的上一个版本。DB_ROW_ID行ID6字节当表没有主键或唯一索引时InnoDB会用该列生成聚簇索引确保每行数据有唯一标识。7.2 回滚日志Undo Log回滚日志是MVCC实现多版本的基础用于保存数据被修改前的旧版本。作用 当事务需要回滚时通过Undo Log恢复数据到修改前的状态支持事务的原子性为MVCC提供数据的历史版本供其他事务读取支持并发读写。生成时机 当事务执行INSERT/UPDATE/DELETE时InnoDB会先将数据的旧版本写入Undo Log再修改实际数据。INSERTUndo Log记录新插入的行信息事务回滚时直接删除该行UPDATE/DELETEUndo Log记录修改前的行数据事务回滚时通过回滚指针恢复旧版本。版本链 多次修改同一行数据时Undo Log会形成一条版本链每次修改后新数据的DB_ROLL_PTR指向旧版本的Undo Log旧版本的DB_ROLL_PTR再指向更早的版本直至最初版本。7.3 ReadView读视图ReadView是事务在读取数据时生成的一个快照用于判断当前事务能看到哪些版本的数据。它本质上是一组用于过滤数据版本的规则包含四个核心参数m_ids当前活跃未提交的事务ID列表。min_trx_id活跃事务中最小的事务ID。max_trx_id系统为下一个事务分配的ID即当前最大事务ID1。creator_trx_id生成该ReadView的事务ID。7.4版本可见性判断规则事务读取数据时会根据ReadView的参数对数据的DB_TRX_ID最后修改事务ID进行判断决定是否可见若DB_TRX_ID creator_trx_id数据是当前事务自己修改的可见。若DB_TRX_ID min_trx_id修改该数据的事务在当前事务启动前已提交可见。若DB_TRX_ID max_trx_id修改该数据的事务在当前事务启动后才开始不可见需通过回滚指针找更早版本。若min_trx_id ≤ DB_TRX_ID ≤ max_trx_id若DB_TRX_ID在m_ids中事务仍活跃不可见需找更早版本若DB_TRX_ID不在m_ids中事务已提交可见。如果当前版本不可见事务会通过DB_ROLL_PTR沿着Undo Log的版本链向上查找直到找到符合规则的可见版本。