H2 Database 事务 Commit 流程实现原理

使用

测试 Demo:

1
2
3
begin;
insert into test values(1);
commit;

功能

模块

总体流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
解析 commit 语句
执行 commit 语句
设置事务状态为已提交
根据 undoLogId 判断事务是否有更改操作,有更改继续提交
cas 设置当前事务状态为已提交到 committingTransactions
遍历 undoLog mvMap
追加标记 undoLog 已提交到 undoLog mvMap 上
获取当前 undoLog 操作涉及的 mvMap id
遍历到对应 Page 的对应 index
将 VersionedValueUncommitted 类型更新成 DefaultRow 类型
清理操作
清理 undoLog mvMap(更新 root 节点为 empty)
清理事务状态
释放 table 锁

代码流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
org.h2.command.dml.TransactionCommand#update
org.h2.engine.SessionLocal#commit
org.h2.mvstore.tx.Transaction#commit
提交事务
org.h2.mvstore.tx.Transaction#markTransactionEnd 
标记事务结束
org.h2.mvstore.tx.Transaction#setStatus 
2.设置事务状态为已提交
1.获取当前 state & logId
org.h2.mvstore.tx.Transaction#getLogId
获取当前 undo log id
org.h2.mvstore.tx.Transaction#getStatus
1.获取当前状态
2.判断当前状态允许变更为目标状态,然后变更为目标状态,此处设置为STATUS_COMMITTED状态
org.h2.mvstore.tx.Transaction#composeState
创建新状态(transaction status + undo log id)
原子变更状态
org.h2.mvstore.tx.Transaction#hasChanges 
3.判断是否有更改(undo log id 不为0)
org.h2.mvstore.tx.TransactionStore#commit 
4.如果有更改,执行提交操作
获取当前事务对应的 undo log mvMap
org.h2.mvstore.MVMap#cursor
获取 undo log mvMap 的 cursor
org.h2.mvstore.tx.TransactionStore#markUndoLogAsCommitted
1.2.标记 undo log 已提交
org.h2.mvstore.tx.TransactionStore#addUndoLogRecord
追加标记 undo log 为 committed(不会修改之前的)
org.h2.mvstore.tx.TransactionStore#getOperationId
2.计算得到 undo log key(transaction id + undo log id)
org.h2.mvstore.MVMap#append
3.追加 undo log mvMap(将 key:undoLogKey, value:undoLogRecord 添加到 map 里)
org.h2.mvstore.MVMap#getRoot
获取根节点
org.h2.mvstore.MVMap#lockRoot
1.锁定根节点
org.h2.mvstore.RootReference#getAppendCounter 
2.获取当前写入下标
4.key 写入 buffer
5.value 写入 buffer
如果buffer超过了,则先将未保存到 btree 页面上的buffer数据保存到 btree 上
org.h2.mvstore.MVMap#unlockRoot 
4.解锁 root
org.h2.mvstore.tx.TransactionStore#flipCommittingTransactionsBit
2.标记事务更改为“已提交”的原子操作(cas 设置当前事务状态为已提交到 committingTransactions)
org.h2.mvstore.Cursor#next
3.遍历当前事务对应的 undoLog mvMap 里的所有 undo log key
org.h2.mvstore.Cursor#getValue
获取 undo log key 和 undo log record
获取 undo log 操作涉及的 map id
org.h2.mvstore.tx.TransactionStore#openMap
5.打开 mvMap(当前事务的当前 undo log 操作对应的行数据map)
获取当前 undo log 操作的 key(比如行主键id)
org.h2.mvstore.tx.CommitDecisionMaker#setUndoKey
org.h2.mvstore.MVMap#operate
根据 undo key 操作 map
org.h2.mvstore.MVMap#flushAndGetRoot
1.获取 mvMap 根节点
org.h2.mvstore.CursorPos#traverseDown 
2.从根节点开始,根据 key 遍历 tree,找到 key 的位置 pos
3.获取操作(插入/删除..)位置对应的 pos 对应的 page
获取当前 key 对应的在 page 上的下标
org.h2.mvstore.Page.Leaf#getValue
4.根据索引获取 page 上的值,如果是插入操作此处为 null,因为原来没有值
org.h2.mvstore.MVMap.DecisionMaker#decide
5.根据 当前值、目标值 和 游标位置 决定操作类型
org.h2.mvstore.tx.CommitDecisionMaker#selectValue
获取当前值(如果是事务commit操作,此处会获取 VersionedValueUncommitted 里的 defaultRow)
org.h2.mvstore.Page#copy
复制当前page
org.h2.mvstore.Page.Leaf#setValue
设置值(更新成DefaultRow)
org.h2.mvstore.RootReference#updateRootPage
更新 root page
org.h2.mvstore.MVMap#clear
清理 undo log mvMap(更新 root 为 empty.可以 debug 查看进入这个方法前后 undoLog 对象的变化)
org.h2.mvstore.tx.TransactionStore#endTransaction
结束事务-清理事务状态
org.h2.engine.SessionLocal#endTransaction
结束事务-释放table锁