H2 Database 事务 Rollback 流程实现原理

使用

1
2
3
4
5
6
7
Connection conn = getConnection();
// 开启事务
conn.setAutoCommit(false);
// 执行 SQL
executeWithLog(conn, "insert into Test(id, name) values(1, 'name1'), (2, 'name2')");
// 回滚事务
conn.rollback();

功能

模块

总体流程

当执行事务回滚时,会获取当前事务所有的 undoLogId,然后从后向前遍历每一个 undoLogId。
对于每一个 undoLogId,先遍历 undoLog btree 找到存储的 Record。Record 信息里包含了当前 undoLogId 操作的数据行信息。
然后根据 Record 信息找到对应记录的 primary key btree,根据主键 id 找到对应行记录进行回滚操作。
当记录回滚完之后,再回滚 undoLog btree 上的 undoLog 信息。

1
2
3
4
5
6
获取 transaction id 和 当前 undo log id.
从当前 undo log id 一直遍历到 0(事务刚开启时 undoLogId=0),回滚当前事务的所有操作.
根据 undo log key 定位到 undo log btree 上的指定位置,获取存储的 record
record 包含当前 undo log 操作的 mvMap(比如 Primary key) 的 mapId, key(比如主键 id), oldValue
再根据 record 打开 primary key mvMap,定位到 row key, 然后决定从 mvMap 上移除当前值
再移除 undo log mvMap 上的当前 undo log key 对应的值

代码流程

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
org.h2.jdbc.JdbcStatement#executeInternal
org.h2.jdbc.JdbcConnection#prepareCommand
解析 rollback 语句
org.h2.command.Command#executeUpdate
org.h2.engine.SessionLocal#setSavepoint
设置savepoint,返回新的logId
org.h2.command.dml.TransactionCommand#update
org.h2.engine.SessionLocal#rollback
org.h2.engine.SessionLocal#hasTransaction
判断在事务中
org.h2.engine.SessionLocal#rollbackTo
执行回滚
org.h2.mvstore.tx.Transaction#rollback
回滚
org.h2.mvstore.tx.Transaction#setStatus
1.尝试设置事务状态为 ROLLED_BACK,并获取之前的事务状态
获取事务当前的 state & logId
org.h2.mvstore.tx.Transaction#getLogId
获取当前 undo log id
org.h2.mvstore.tx.Transaction#getStatus
获取事务当前状态
2.判断当前状态允许变更为目标状态,然后变更为目标状态
org.h2.mvstore.tx.Transaction#composeState 新状态(transaction status + undo log id)
3.原子变更 statusAndLogId
获取当前 status 和 logId
org.h2.mvstore.tx.TransactionStore#rollbackTo
回滚
org.h2.mvstore.tx.RollbackDecisionMaker#init0
创建 rollback decision maker
3.遍历从 maxLogId 到 toLogId,依次进行回滚操作
org.h2.mvstore.tx.TransactionStore#getOperationId
3.1.计算 undo log key (transaction id + log id)
org.h2.mvstore.MVMap#operate
3.2.根据 undo log key 操作 undo log mvMap, 执行回滚操作
org.h2.mvstore.MVMap#flushAndGetRoot
获取 undo log mvMap 根节点
org.h2.mvstore.CursorPos#traverseDown
找到 undoLog 位置
org.h2.mvstore.MVMap.DecisionMaker#decide
内部会先回滚该 undoLog 对应的 mvMap上 对应 row
org.h2.mvstore.tx.RollbackDecisionMaker#decide
1.获取 undo log record
里当前存储的旧值
org.h2.mvstore.tx.TransactionStore#openMap
3.获取数据行对应 mvMap(比如 primary key mvMap)
org.h2.mvstore.MVMap#operate
回滚数据行
org.h2.mvstore.MVMap#flushAndGetRoot
获取表根节点
org.h2.mvstore.CursorPos#traverseDown
找到数据行
org.h2.mvstore.Page.Leaf#remove
移除数据行
org.h2.mvstore.Page.Leaf#remove
移除 undo log 叶子节点
org.h2.mvstore.tx.TransactionStore#endTransaction
org.h2.mvstore.tx.Transaction#setStatus
修改状态
org.h2.engine.SessionLocal#endTransaction
org.h2.engine.SessionLocal#unlockAll
释放锁

参考