拓冰建站拓冰建站
首页 / 资讯中心 / 正文

TiDB 如何用 SAVEPOINT 与 ROLLBACK TO SAVEPOINT 回滚事务中的部分修改

TiDB 如何用 SAVEPOINT 与 ROLLBACK TO SAVEPOINT 回滚事务中的部分修改【免费下载链接】tidbTiDB is built for agentic workloads that grow unpredictably, with ACID guarantees and native support for transactions, analytics, and vector search. No data silos. No noisy neighbors. No infrastructure ceiling.项目地址: https://gitcode.com/GitHub_Trending/ti/tidb一个对账批处理任务跑到长事务第 50 步发现第 48 步写错了数据。直接回滚整个事务前 47 步全部作废直接提交错误数据落库。这就是 ORM 框架如 gorm常要靠变通方案绕开的部分回滚场景。TiDB 用标准 SQL 保存点SAVEPOINT解决它事务内执行SAVEPOINT记录检查点出问题时用ROLLBACK TO SAVEPOINT只撤销检查点之后的写入事务本身继续存活还能接着写、正常提交。最小可用的保存点回滚片段前置条件是有一张表。下面这段在任何 TiDB 会话都能直接跑通DROP TABLE IF EXISTS t; CREATE TABLE t(id int, a int, UNIQUE INDEX idx(id)); BEGIN PESSIMISTIC; INSERT INTO t VALUES (1, 1); SAVEPOINT s1; INSERT INTO t VALUES (2, 2); -- 假设这一步写错了 ROLLBACK TO s1; COMMIT; SELECT * FROM t;输出id a 1 1(1,1) 落库(2,2) 被撤销。ROLLBACK TO只回退数据不结束事务所以COMMIT照常执行。乐观模式同样支持把BEGIN PESSIMISTIC换成BEGIN即可。仓库测试对同一组语句在乐观、悲观两种模式都跑过一遍见 pkg/executor/test/txn/txn_test.go#L259-L338。悲观事务下保存点回滚的完整走查以下步骤取自仓库集成测试脚本 tests/integrationtest/t/executor/executor_txn.test#L1-L24预期输出取自 tests/integrationtest/r/executor/executor_txn.result#L1-L32。第 1 步建表写入两行中间隔一个保存点。DROP TABLE IF EXISTS t; CREATE TABLE t(id int, a int, UNIQUE INDEX idx(id)); BEGIN PESSIMISTIC; INSERT INTO t VALUES (1, 1); SAVEPOINT s1; INSERT INTO t VALUES (2, 2);-- 全部成功无输出(1,1) 属于保存点之前(2,2) 属于保存点之后。第 2 步回滚到 s1然后继续写验证事务还活着。ROLLBACK TO s1; INSERT INTO t VALUES (2, 2); SELECT * FROM t;id a 1 1 2 2回滚只把事务内存数据库MemDB回退到检查点会话的事务状态没变所以还能插入新的 (2,2)。这是它与整体回滚ROLLBACK的本质区别。第 3 步对同一个保存点再回滚一次验证可重复使用。ROLLBACK TO s1; SELECT * FROM t;id a 1 1第二次回滚撤销的正是第 2 步重新插入的 (2,2)。第 4 步提交确认落盘内容。COMMIT; SELECT * FROM t;id a 1 1只有保存点之前的写入进了存储。第 5 步保存点之前有删除时删除不会被回滚。DELETE FROM t; INSERT INTO t VALUES (1, 1); BEGIN PESSIMISTIC; DELETE FROM t WHERE id 1; SAVEPOINT s1; INSERT INTO t VALUES (1, 2); ROLLBACK TO s1; SELECT * FROM t;id a空结果集。保存点之前的 DELETE 保留只有保存点之后的 INSERT 被撤销。COMMIT之后表依然是空的。保存点栈的存储与裁剪规则保存点记录在事务上下文TransactionContext.Savepoints这个有序切片上不是全局表。每条记录包含两份快照MemDB 检查点和事务上下文快照表变更量 TableDeltaMap、悲观锁缓存、缓存表引用定义见 pkg/sessionctx/variable/session.go#L482-L515。四条裁剪规则决定后续语句能不能执行同名覆盖AddSavepoint先删同名记录再追加到切片末尾。重复执行SAVEPOINT s1后s1 的位置从原来的下标挪到栈尾。回滚截断ROLLBACK TO s2把切片截成保留 s2 及其之前s2 之后的保存点消失s2 本身保留、可复用。释放截断RELEASE SAVEPOINT s2删掉 s2 及其之后的所有保存点但不动数据不提交也不回滚。事务外静默失效不在事务中且 autocommit 开启时SAVEPOINT直接返回不记录任何东西后续ROLLBACK TO报错。判断逻辑在 pkg/executor/simple.go#L662-L678。保存点名的大小写AddSavepoint、RollbackToSavepoint、ReleaseSavepoint三个函数查找前都先strings.ToLower所以名称实际大小写不敏感只有报错信息里的名称保留书写时的原样。测试用例savepoint S1存储后变成s1rollback to S3能命中证据在 pkg/executor/test/txn/txn_test.go#L273-L285。ROLLBACK TO SAVEPOINT的执行路径在executeRollback先从TxnCtx取记录再调txn.RollbackMemDBToCheckpoint回退内存数据库见 pkg/executor/simple.go#L795-L812。与 MySQL 保存点行为的两处差异设计文档 docs/design/2022-07-22-transaction-savepoint.md#L121-L137 的 MySQL Compatibility 一节明确列出了差异维度TiDBMySQL锁的释放时机保存点之后持有的悲观锁不随回退释放等事务提交或整体回滚时统一释放ROLLBACK TO SAVEPOINT时立即释放保存点之后的锁AUTO_INCREMENT / SEQUENCE回退不回收已分配值提交后出现空洞同样不回收行为一致锁时机差异有实际后果回退保存点后其他会话对同一行SELECT ... FOR UPDATE仍会阻塞到本事务结束。仓库有专门的双会话测试会话 1 回退到保存点后会话 2 的FOR UPDATE依然被挡住直到会话 1 真正ROLLBACK才放行见 pkg/executor/test/txn/txn_test.go#L469-L513。第 499 行的注释原文是 After rollback to s1, should not release lock in the row which a 1。如何确认保存点回滚真的生效没有 SQL 能直接查询保存点列表只能用三种间接检查事务内对比ROLLBACK TO前后各执行一次SELECT保存点之后出现的行应该消失。报错探针对可疑名称再执行一次ROLLBACK TO或RELEASE SAVEPOINT。保存点不存在会报[executor:1305]SAVEPOINT s1 does not exist。这是判断某个保存点是否还存在的唯一途径。提交后对比COMMIT之后再次SELECT确认落盘数据只包含最后一次回滚点之前的写入。报错时按两个位置排查。第一处是前置条件executeSavepoint开头三个分支见 pkg/executor/simple.go#L659-L678。第二处是模式行为差异对照单测里的期望报错串见 pkg/executor/test/txn/txn_test.go#L259-L338。约束变量可以直接查SELECT tidb_constraint_check_in_place_pessimistic;保存点限制条件与环境清单限制现象报错原文如何检查或规避实例开启 binlogSAVEPOINT is not supported when binlog is enabledTiDB 4.0 之后已不建议使用 binlog确认实例未开启不在显式事务中autocommitSAVEPOINT静默成功ROLLBACK TO报[executor:1305]SAVEPOINT s1 does not exist用BEGIN包裹业务语句悲观事务且 in-place constraint check 关闭savepoint is not supported in pessimistic transactions when in-place constraint check is disabled查tidb_constraint_check_in_place_pessimistic变量定义见 pkg/sessionctx/vardef/tidb_vars.go#L1033同名保存点旧记录被删、重新追加到栈尾栈位置会变不要按创建顺序做假设悲观锁释放回退保存点后锁不释放对照 pkg/executor/test/txn/txn_test.go#L469-L513AUTO_INCREMENT / SEQUENCE值不回收提交后有空洞与 MySQL 一致预期行为保存点名大小写不敏感小写存储报错显示原样书写不要用仅大小写不同的名字区分语义功能出处是 2022 年的保存点设计文档 docs/design/2022-07-22-transaction-savepoint.md可用SELECT VERSION();确认你的实例版本是否包含该特性。【免费下载链接】tidbTiDB is built for agentic workloads that grow unpredictably, with ACID guarantees and native support for transactions, analytics, and vector search. No data silos. No noisy neighbors. No infrastructure ceiling.项目地址: https://gitcode.com/GitHub_Trending/ti/tidb创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门