注册
达梦数据库的事务机制
专栏/技术分享/ 文章详情 /

达梦数据库的事务机制

DM_336625 2026/06/04 112 0 0
摘要

一、达梦MVCC的整体架构

1.1 MVCC的核心设计理念

达梦数据库采用基于回滚段的MVCC方案,其核心策略是:数据页中只保留物理记录的最新版本,通过回滚记录(Undo Record)维护数据的历史版本。这种设计使得读操作可以不受写操作阻塞,写操作也不会影响读操作的进行,极大提升了数据库的并发性能。

与传统悲观锁机制不同,达梦的MVCC默认认为冲突不常发生,写操作创建数据的新版本,读操作访问旧版本,从而实现了读写并发的目标。

1.2 两种MVCC工作模式

达梦数据库为用户提供了两种MVCC模式,通过参数TRX_VIEW_MODE进行控制:

模式0:基于回滚记录的MVCC

  • 数据行包含事务ID(TID)和版本指针(RPTR),记录间单向连接,形成链式版本结构
  • 事务根据当前活动事务的视图,沿着链式版本结构获取可见记录集合

模式1:基于时间戳的MVCC(默认模式)

  • 全局维护一个大数组CMTARR(Commit Array),长度由参数TRX_CMTARR_SIZE控制
  • 事务启动时获取时间戳SNAP_CMTSEQ,提交时将CMTARR中对应位置设置为当前时间
  • 数据版本的可见性通过比较SNAP_CMTSEQ和CMTSEQ来判断,效率更高、内存开销更低

基于时间戳的MVCC是达梦数据库当前版本的默认模式,它通过避免回滚段链的遍历查找,大幅提升了可见性判断的性能。

二、多版本控制的核心机制

2.1 物理记录的双字段设计

达梦MVCC的实现基础是每一条物理记录中包含的两个关键字段:TIDRPTR

字段 全称 作用 存储内容
TID Transaction ID 记录修改该行的事务号 最后一次修改该行的事务ID
RPTR Roll Pointer 指向前一个版本 回滚段中上一个版本回滚记录的物理地址

这两个字段的实现细节如下:

  • 对于索引组织表(达梦默认表类型),系统会自动添加TRXIDROLPTR两个隐藏列
  • TRXID记录最近更新或插入这条记录的事务ID
  • ROLPTR指向被写在回滚段中的Undo Log记录,当事务回滚时可以通过它找到之前版本的数据
  • 该行记录的所有旧版本,在Undo Log中通过链表形式组织,将多个版本的值串联在一起

2.2 版本链的构建机制

当对数据进行修改操作时,达梦的版本链构建过程如下:

INSERT操作

  • 新插入的物理记录获得当前事务的TID
  • 由于没有更老的版本,回滚记录的RPTR值为NULL

UPDATE/DELETE操作

  • 修改前,将原始数据的镜像写入回滚段,生成回滚记录
  • 回滚记录中的TID保存原始物理记录上的TID值(上一个版本的事务号)
  • 回滚记录的RPTR指向更早版本的回滚记录地址
  • 物理记录更新TID为当前事务号,RPTR指向刚生成的回滚记录

这样,每个数据行通过物理记录和多个回滚记录,形成一个从最新版本到最初版本的完整版本链。各版本之间的关系可以用以下结构表示:

text

最新物理记录 ←─RPTR── 回滚记录V1 ←─RPTR── 回滚记录V2 ←─RPTR── ... ←─NULL
   (TID=N)           (TID=N-1)           (TID=N-2)

2.3 回滚记录的管理

回滚记录与物理记录结构类似,同样包含TID和RPTR两个字段:

  • TID:保存产生回滚记录时物理记录上的TID值(即上一个版本的事务号)
  • RPTR:保存回滚段中上一个版本回滚记录的物理地址

回滚记录存放在回滚表空间(ROLL Tablespace)的回滚段中。回滚段由一定数量的回滚页组成,回滚页存放的是一批回滚记录。回滚记录有专属的格式,与物理记录格式不同。

三、MVCC的可见性判断

3.1 活动事务视图(ReadView)

达梦通过活动事务视图(也称为ReadView)来判断数据版本的可见性。该视图根据事务隔离级别的不同,在以下时机收集:

  • 读已提交(Read Committed)级别:在每个SQL语句执行时收集一次活动事务信息
  • 串行化(Serializable)级别:在事务启动时收集一次活动事务信息

活动事务视图的核心包含以下信息:

  • 活动事务列表:当前所有未提交事务的事务ID集合
  • NEXT_TID:系统中即将产生的下一个事务号
  • MIN_TRXID:所有活动事务中最小的事务ID
  • MAX_TRXID:系统已分配的最大事务ID+1

3.2 可见性判断的三条规则

达梦数据库的多版本可见性判断遵循以下核心规则:

规则 条件 判断结果 典型场景
规则1 物理记录的TID == 当前事务号 可见 本事务自己修改的数据
规则2 TID不在活动事务表中 TID < NEXT_TID 可见 其他已提交事务的数据
规则3 TID在活动事务表中 TID ≥ NEXT_TID 不可见 未提交事务或未来事务的数据

如果当前物理记录不可见,系统会按照该记录的RPTR指示,沿着版本链找到上一个版本,再次应用上述规则进行判断,直到找到可见版本为止。

3.3 可见性判断实例分析

以下通过一个具体案例来说明可见性判断的完整过程:

场景设置

  • 事务A(TID=80170)执行UPDATE操作但未提交
  • 事务B(TID=80169)执行SELECT查询操作

判断过程

  1. 事务B查询数据:读取物理记录,发现其TID=80170

  2. 获取活动事务视图:当前活动事务列表包含{80170},NEXT_TID=80171

  3. 应用判断规则

    • 规则1:TID=80170 ≠ 80169 → 不满足
    • 规则2:TID=80170在活动事务表中 → 不满足
    • 规则3:TID在活动事务表中 → 满足,不可见
  4. 沿着RPTR查找:找到回滚记录,其TID=80168(上一版本的事务号)

  5. 再次判断:TID=80168不在活动事务表中且 < NEXT_TID → 可见

  6. 返回结果:事务B看到的是修改前的旧版本数据

当事务A提交后,活动事务列表变为空,此时事务B再次查询时,物理记录的TID=80170不在活动事务表中且 < NEXT_TID,根据规则2变为可见,因此能看到更新后的数据。

四、创新的TID锁机制

4.1 传统行锁的问题

传统数据库通常使用行锁进行并发访问控制,每个修改操作都需要对物理记录加锁。这种方式会消耗大量锁资源,产生频繁的锁冲突,特别是在高并发场景下,锁管理本身就成为性能瓶颈。

4.2 TID锁的设计原理

达梦数据库采用了一种独特的封锁机制——TID锁,以事务号为封锁对象,完全消除了传统行锁的开销。

TID锁的核心设计如下:

  • 每个事务启动时,自动以独占(X)方式对当前事务号进行封锁
  • 事务号是全局唯一的,因此TID锁不存在冲突,总是可以封锁成功
  • 执行INSERT、DELETE、UPDATE操作修改物理记录时,设置事务号到TID字段的动作,就相当于隐式地对物理记录上了一把X方式的TID锁

4.3 TID锁的优势

通过这种设计,达梦实现了:

  1. 无需额外行锁:所有修改物理记录的操作不再需要额外的行锁,避免了大量行锁对系统资源的消耗
  2. 减少封锁冲突:TID锁本身就代表了对记录的修改权限,不需要再通过锁管理器协调
  3. 提升集群性能:在DMDSC(达梦共享存储集群)环境中,封锁代价更高,TID锁的优势更加明显

DMDSC集群将封锁管理拆分为全局封锁服务(GLS)和本地封锁服务(LLS)两部分,控制节点统一处理所有封锁请求,进行死锁检测。同时,达梦还将数据字典锁和表锁合并为对象锁,进一步减少封锁冲突。

五、回滚机制的完整实现

5.1 Undo日志的生成与管理

达梦的回滚机制基于Undo日志(回滚日志)实现。当事务执行修改操作时,系统会先将数据的旧版本写入Undo日志,然后再修改实际数据。

Undo日志的记录类型

  • INSERT操作:Undo Log记录新插入的行信息,回滚时直接删除该行
  • UPDATE/DELETE操作:Undo Log记录修改前的行数据,回滚时通过回滚指针恢复旧版本

5.2 回滚表空间与回滚段

达梦使用固定的回滚表空间ROLL来存放所有回滚记录:

  • 回滚表空间由多个回滚段(Segment)组成
  • 每个回滚段包含一定数量的回滚页
  • 在DMDSC集群中,为每个节点分配单独的回滚段,减少并发冲突
  • 回滚段的数据不会永久保留,事务结束后由PURGE模块负责释放

Redo日志对回滚段的保护:Redo日志记录了所有对数据库数据的修改,包括对回滚段的修改。当使用重做日志恢复数据库时,系统会重做回滚段中修改了的数据,确保回滚记录不丢失。

5.3 事务回滚的执行过程

当事务执行ROLLBACK时,达梦的回滚机制如下:

  1. 扫描该事务所产生的所有回滚记录
  2. 按照与执行顺序相反的方向,依次应用回滚记录
  3. 对于UPDATE操作,将回滚记录中的旧值写回物理记录
  4. 对于INSERT操作,删除对应的物理记录
  5. 对于DELETE操作,恢复被删除的物理记录
  6. 释放该事务所占用的回滚页空间

如果回滚过程中遇到存储空间不足等异常,达梦会将事务状态调整为回滚挂起(Rollback Suspended) ,事务保留在系统中并持有锁资源,系统每5分钟自动尝试继续回滚。

5.4 系统故障恢复

当系统崩溃后重新启动时,达梦通过Redo和Undo的协同完成故障恢复:

  1. 从最近的一个检查点开始扫描联机日志
  2. 重做阶段:重做日志记录的内容,将系统恢复到故障发生前的状态
  3. 回滚阶段:将仍然活动的事务利用回滚段进行回滚,撤销未提交的修改

这套机制保证了事务的原子性和持久性:系统故障发生时,仍然活动的事务被安全回滚,已提交事务的修改则确保持久化。

六、隔离级别与MVCC的集成

达梦数据库支持三种事务隔离级别,默认采用读已提交(READ COMMITTED)

隔离级别 ReadView收集时机 特性 可见性
读未提交 不收集 可能读到未提交数据 所有物理记录都可见
读已提交(默认) 每条语句执行时 避免脏读,可能出现不可重复读 每次查询获取最新快照
串行化 事务启动时 避免脏读、不可重复读、幻读 整个事务使用同一快照

串行化隔离级别的特殊处理:如果一个串行化事务试图更新或删除数据,而这些数据在此事务开始后被其他事务修改并提交,达梦会报"串行化事务被打断"错误。

七、基于回滚的闪回技术

达梦数据库利用回滚段中保存的Undo记录,提供了闪回查询功能。

闪回的核心原理

  • 通过参数ENABLE_FLASHBACK开启闪回功能(默认关闭)
  • 参数UNDO_RETENTION控制回滚段保留时间(默认90秒),决定可闪回的时间范围
  • 达梦在内存中记录每个事务的起始时间和提交时间
  • 根据用户指定的时刻,结合当前记录和回滚段中的UNDO记录,还原历史数据

闪回的使用方式

sql

-- 基于时间戳闪回查询
SELECT * FROM table_name WHEN TIMESTAMP '2024-10-02 10:26:31';
SELECT * FROM table_name AS OF TIMESTAMP '2024-10-02 10:26:31';

-- 基于LSN闪回查询
SELECT * FROM table_name AS OF LSN 47529;

-- 闪回表(恢复数据)
FLASHBACK TABLE table_name TO TIMESTAMP '2024-10-02 10:26:31';
FLASHBACK TABLE table_name TO LSN 45412;

闪回的限制

  • 只能闪回到UNDO_RETENTIN指定的时间范围内
  • 不能恢复DROP等对象级误操作
  • 表结构发生变化后无法闪回
  • 系统表、临时表等不支持闪回

达梦社区技术 https://eco.dameng.com

评论
后发表回复

作者

文章

阅读量

获赞

扫一扫
联系客服