ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

TiDB 使用 SAVEPOINT 实现部分回滚的完整指南

TiDB 使用 SAVEPOINT 实现部分回滚的完整指南 TiDB 使用 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/tidbTiDB开源分布式数据库支持标准保存点SAVEPOINT语法在事务内标记检查点后可只撤销其后写入的数据修改而事务不结束。本文面向使用 ORM 框架实现部分回滚的开发者以及需要排查保存点报错的 DBA。这个功能解决什么问题设想一个长事务先扣减库存再写订单最后写积分日志。执行到写订单时撞上了唯一键冲突希望撤掉订单这一步的写入但保留前面的扣减与日志。直接ROLLBACK会把事务内全部改动一并丢弃而逐条手工反向写入既繁琐又容易出错。TiDB 的做法是让SAVEPOINT在显式事务内打一个检查点ROLLBACK TO SAVEPOINT仅回退检查点之后的数据修改事务本身继续可用乐观、悲观两种模式均支持。该能力的设计初衷之一就是兼容 gorm 等 ORM 框架的部分回滚用法2022-07-22-transaction-savepoint.md#L13。支持矩阵使用场景是否可用前置条件触发报错显式事务内乐观模式可用已执行BEGIN—显式事务内悲观模式可用会话变量tidb_constraint_check_in_place_pessimistic为 ONsavepoint is not supported in pessimistic transactions when in-place constraint check is disabled事务外执行SAVEPOINTautocommit 开启静默忽略no-op无无后续ROLLBACK TO报[executor:1305]SAVEPOINT s1 does not exist启用 binlog 的实例不可用—SAVEPOINT is not supported when binlog is enabled临时表、ON COMMIT DELETE ROWS全局临时表可用位于显式事务内—回收已分配的 AUTO_INCREMENT / SEQUENCE 值不可回收—无报错提交后出现值空洞依据simple.go#L662-L678no-op 与悲观模式检查、simple.go#L660binlog 报错、txn_test.go#L319-L322三种事务模式下同一组用例、executor_txn.test#L39-L131临时表与全局临时表、设计文档 Warning 段。上手示例订单修正流程中的部分回滚下面以「首行写对、次行写错需撤掉次行并改写」为例。场景改编自仓库集成测试TestRollbackToSavepointexecutor_txn.test#L1-L24表名与数据已替换。第 1 步建表并进入悲观事务写入正确行后打点。CREATE TABLE order_record(order_id INT, amount INT, UNIQUE INDEX uk(order_id)); BEGIN PESSIMISTIC; INSERT INTO order_record VALUES (1001, 50); SAVEPOINT sp_safe; INSERT INTO order_record VALUES (1002, 999);第 2 步回退到检查点错误写入被撤销事务未结束。ROLLBACK TO sp_safe; SELECT * FROM order_record;示例结果order_id amount 1001 50第 3 步继续在同一事务中补写正确值并再次回退验证保存点可复用。INSERT INTO order_record VALUES (1002, 80); SELECT * FROM order_record; ROLLBACK TO sp_safe; SELECT * FROM order_record;示例结果前两次查询各一行表头order_id amount 1001 50 1002 80order_id amount 1001 50第 4 步提交并确认最终落盘内容。INSERT INTO order_record VALUES (1002, 80); COMMIT; SELECT * FROM order_record;示例结果order_id amount 1001 50 1002 80注意第 3 步中第二次ROLLBACK TO sp_safe把刚补写的 (1002, 80) 也回退了回退只保留目标点自身目标点之后的写入一律撤销session.go#L542-L552因此实际流程中补写应放在最后一次回退之后。行为细节与易错点保存点名不区分大小写。现象创建s1后再执行SAVEPOINT S1点列表中仍只有小写s1且旧点被替换不会新增第二个点txn_test.go#L273-L279 中用例{savepoint S1, []string{s2, s3, s1}}可直接复现。 原因名称在匹配时统一转小写session.go#L543同名再打点时旧记录先删除再追加。 正确做法不要依赖s1/S1来区分两个检查点改用不同的名称。ROLLBACK TO会截断目标点之后的全部检查点。现象依次创建 s1、s2、s3 后执行ROLLBACK TO s2此时再回退或释放 s3 均报[executor:1305]SAVEPOINT s3 does not existtxn_test.go#L277-L282。 原因回退逻辑把点列表裁剪到目标点下标为止session.go#L547目标点之后的记录一并清除。 正确做法把保存点当栈来设计若后续流程仍要回退某一步回退之后重新SAVEPOINT。同一保存点可反复回退。现象连续两次ROLLBACK TO s1都成功且每次都回到 s1 时刻的状态executor_txn.test#L8-L12 与 executor_txn.result#L7-L16 的成对输出。 原因目标点自身在截断后仍保留在列表中。 正确做法把最靠前的保存点当作可重复使用的「最后安全点」多次试错后统一回退到它。RELEASE SAVEPOINT不触碰数据。现象释放某点后表数据与释放前完全一致仅该点及其之后的点被移除后续回退到这些点才报错txn_test.go#L309-L315。 原因release 只是从列表中删除记录既不提交也不回滚设计文档 RELEASE 段。 正确做法只对确定不再需要的检查点执行释放避免误删后续还要回退的点。事务外的SAVEPOINT是静默 no-op。现象autocommit 开启且未BEGIN时SAVEPOINT s1无任何报错紧接着ROLLBACK TO s1却报[executor:1305]SAVEPOINT s1 does not existtxn_test.go#L270-L274。 原因executeSavepoint检测到「不在事务中且 autocommit 开启」时直接返回simple.go#L665-L667点根本没有记录。 正确做法把BEGIN作为打点的前置动作不要依赖 SAVEPOINT 语句本身来确认是否处于事务。与 MySQL 的行为差异锁释放时机与自增值悲观锁不随回退释放。MySQL 在执行ROLLBACK TO SAVEPOINT时会释放该保存点之后持有的行锁TiDB 悲观事务在回退时刻不逐步放锁而是等事务提交或整体回滚时才统一释放设计文档 MySQL Compatibility 段。实际影响部分回退之后其他会话对这些行的SELECT ... FOR UPDATE仍会阻塞到本事务结束排查时不要将其误判为死锁或保存点失效。自增值不回收与 MySQL 一致属提醒项。ROLLBACK TO不回收已分配的 AUTO_INCREMENT / SEQUENCE 值提交后序列出现空洞设计文档 Warning 段。这条与 MySQL 行为相同但因常被误解为 bug单独列出。报错排查清单按序定位保存点问题看到SAVEPOINT is not supported when binlog is enabled确认实例是否启用 binlogsimple.go#L660。binlog 自 TiDB 4.0 起已不建议使用设计文档可关闭 binlog 或改用其他数据同步链路。看到savepoint is not supported in pessimistic transactions when in-place constraint check is disabled当前处于悲观事务且约束检查被关闭把会话变量tidb_constraint_check_in_place_pessimistic置为 ONsimple.go#L668-L670。看到[executor:1305]SAVEPOINT s1 does not exist按序检查是否已BEGIN事务外打点会被静默忽略名字是否写错保存点名不区分大小写且同名会被覆盖先确认实际创建过的名称该点是否已被更早一次ROLLBACK TO或RELEASE SAVEPOINT截断/删除事务是否已经 COMMIT点随事务一起消失。打了点但数据未被回退确认出问题的写入确实发生在SAVEPOINT之后回退前后各执行一次事务内SELECT对比行数COMMIT后再查一次落盘结果三段输出应分别反映「回退前 / 回退后 / 最终」状态。其他会话FOR UPDATE在部分回退后仍阻塞属于 TiDB 的锁释放时机见上文差异一节等待事务结束即可无需干预保存点。无法用 SQL 直接查看保存点列表只能通过再次执行ROLLBACK TO或RELEASE SAVEPOINT是否报does not exist来间接判断某个点是否仍在。【免费下载链接】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),仅供参考
返回列表