达梦数据库采用基于回滚段的MVCC方案,其核心策略是:数据页中只保留物理记录的最新版本,通过回滚记录(Undo Record)维护数据的历史版本。这种设计使得读操作可以不受写操作阻塞,写操作也不会影响读操作的进行,极大提升了数据库的并发性能。
与传统悲观锁机制不同,达梦的MVCC默认认为冲突不常发生,写操作创建数据的新版本,读操作访问旧版本,从而实现了读写并发的目标。
达梦数据库为用户提供了两种MVCC模式,通过参数TRX_VIEW_MODE进行控制:
模式0:基于回滚记录的MVCC
模式1:基于时间戳的MVCC(默认模式)
TRX_CMTARR_SIZE控制基于时间戳的MVCC是达梦数据库当前版本的默认模式,它通过避免回滚段链的遍历查找,大幅提升了可见性判断的性能。
达梦MVCC的实现基础是每一条物理记录中包含的两个关键字段:TID和RPTR。
| 字段 | 全称 | 作用 | 存储内容 |
|---|---|---|---|
| TID | Transaction ID | 记录修改该行的事务号 | 最后一次修改该行的事务ID |
| RPTR | Roll Pointer | 指向前一个版本 | 回滚段中上一个版本回滚记录的物理地址 |
这两个字段的实现细节如下:
TRXID和ROLPTR两个隐藏列TRXID记录最近更新或插入这条记录的事务IDROLPTR指向被写在回滚段中的Undo Log记录,当事务回滚时可以通过它找到之前版本的数据当对数据进行修改操作时,达梦的版本链构建过程如下:
INSERT操作:
UPDATE/DELETE操作:
这样,每个数据行通过物理记录和多个回滚记录,形成一个从最新版本到最初版本的完整版本链。各版本之间的关系可以用以下结构表示:
text
最新物理记录 ←─RPTR── 回滚记录V1 ←─RPTR── 回滚记录V2 ←─RPTR── ... ←─NULL
(TID=N) (TID=N-1) (TID=N-2)
回滚记录与物理记录结构类似,同样包含TID和RPTR两个字段:
回滚记录存放在回滚表空间(ROLL Tablespace)的回滚段中。回滚段由一定数量的回滚页组成,回滚页存放的是一批回滚记录。回滚记录有专属的格式,与物理记录格式不同。
达梦通过活动事务视图(也称为ReadView)来判断数据版本的可见性。该视图根据事务隔离级别的不同,在以下时机收集:
活动事务视图的核心包含以下信息:
达梦数据库的多版本可见性判断遵循以下核心规则:
| 规则 | 条件 | 判断结果 | 典型场景 |
|---|---|---|---|
| 规则1 | 物理记录的TID == 当前事务号 | 可见 | 本事务自己修改的数据 |
| 规则2 | TID不在活动事务表中 且 TID < NEXT_TID | 可见 | 其他已提交事务的数据 |
| 规则3 | TID在活动事务表中 或 TID ≥ NEXT_TID | 不可见 | 未提交事务或未来事务的数据 |
如果当前物理记录不可见,系统会按照该记录的RPTR指示,沿着版本链找到上一个版本,再次应用上述规则进行判断,直到找到可见版本为止。
以下通过一个具体案例来说明可见性判断的完整过程:
场景设置:
判断过程:
事务B查询数据:读取物理记录,发现其TID=80170
获取活动事务视图:当前活动事务列表包含{80170},NEXT_TID=80171
应用判断规则:
沿着RPTR查找:找到回滚记录,其TID=80168(上一版本的事务号)
再次判断:TID=80168不在活动事务表中且 < NEXT_TID → 可见
返回结果:事务B看到的是修改前的旧版本数据
当事务A提交后,活动事务列表变为空,此时事务B再次查询时,物理记录的TID=80170不在活动事务表中且 < NEXT_TID,根据规则2变为可见,因此能看到更新后的数据。
传统数据库通常使用行锁进行并发访问控制,每个修改操作都需要对物理记录加锁。这种方式会消耗大量锁资源,产生频繁的锁冲突,特别是在高并发场景下,锁管理本身就成为性能瓶颈。
达梦数据库采用了一种独特的封锁机制——TID锁,以事务号为封锁对象,完全消除了传统行锁的开销。
TID锁的核心设计如下:
通过这种设计,达梦实现了:
DMDSC集群将封锁管理拆分为全局封锁服务(GLS)和本地封锁服务(LLS)两部分,控制节点统一处理所有封锁请求,进行死锁检测。同时,达梦还将数据字典锁和表锁合并为对象锁,进一步减少封锁冲突。
达梦的回滚机制基于Undo日志(回滚日志)实现。当事务执行修改操作时,系统会先将数据的旧版本写入Undo日志,然后再修改实际数据。
Undo日志的记录类型:
达梦使用固定的回滚表空间ROLL来存放所有回滚记录:
Redo日志对回滚段的保护:Redo日志记录了所有对数据库数据的修改,包括对回滚段的修改。当使用重做日志恢复数据库时,系统会重做回滚段中修改了的数据,确保回滚记录不丢失。
当事务执行ROLLBACK时,达梦的回滚机制如下:
如果回滚过程中遇到存储空间不足等异常,达梦会将事务状态调整为回滚挂起(Rollback Suspended) ,事务保留在系统中并持有锁资源,系统每5分钟自动尝试继续回滚。
当系统崩溃后重新启动时,达梦通过Redo和Undo的协同完成故障恢复:
这套机制保证了事务的原子性和持久性:系统故障发生时,仍然活动的事务被安全回滚,已提交事务的修改则确保持久化。
达梦数据库支持三种事务隔离级别,默认采用读已提交(READ COMMITTED) :
| 隔离级别 | ReadView收集时机 | 特性 | 可见性 |
|---|---|---|---|
| 读未提交 | 不收集 | 可能读到未提交数据 | 所有物理记录都可见 |
| 读已提交(默认) | 每条语句执行时 | 避免脏读,可能出现不可重复读 | 每次查询获取最新快照 |
| 串行化 | 事务启动时 | 避免脏读、不可重复读、幻读 | 整个事务使用同一快照 |
串行化隔离级别的特殊处理:如果一个串行化事务试图更新或删除数据,而这些数据在此事务开始后被其他事务修改并提交,达梦会报"串行化事务被打断"错误。
达梦数据库利用回滚段中保存的Undo记录,提供了闪回查询功能。
闪回的核心原理:
ENABLE_FLASHBACK开启闪回功能(默认关闭)UNDO_RETENTION控制回滚段保留时间(默认90秒),决定可闪回的时间范围闪回的使用方式:
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;
闪回的限制:
达梦社区技术 https://eco.dameng.com
文章
阅读量
获赞
