news 2026/9/13 4:38:02

TiDB 如何用保存点(SAVEPOINT)回滚事务中的部分修改

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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/tidb

一批对账任务在长事务里写到一半时校验失败,整条ROLLBACK会把前面已核对通过的几千行写入全部作废。TiDB 保存点提供标准保存点语法:在事务内打一个检查点,用ROLLBACK TO SAVEPOINT只撤销其后的修改,事务本身保持可用,可以继续写入并提交。乐观、悲观两种事务模式都支持,但有三个前置约束:必须处于显式事务中、实例不能开启 binlog、悲观事务要求 in-place constraint check 开启。

快速开始:最小可运行示例

这步在悲观事务中演示保存点撤销部分写入:

DROP TABLE IF EXISTS t_batch; CREATE TABLE t_batch(id int, val int, UNIQUE INDEX idx(id)); BEGIN PESSIMISTIC; INSERT INTO t_batch VALUES (1, 1); SAVEPOINT s1; INSERT INTO t_batch VALUES (2, 2); -- 假设这行数据有问题 ROLLBACK TO s1; -- 只撤销 (2,2) INSERT INTO t_batch VALUES (2, 20); -- 事务仍可用 COMMIT; SELECT * FROM t_batch;

预期输出(示例结果):

id val 1 1 2 20

使用前的检查项

检查项不满足时的表现仓库内出处
binlog 必须未启用SAVEPOINT is not supported when binlog is enabledpkg/executor/simple.go#L659-L660、设计文档 2022-07-22-transaction-savepoint.md 的 Warning 节
必须处于显式事务(BEGIN之后)无报错,保存点被静默忽略,后续ROLLBACK TO[executor:1305]SAVEPOINT s1 does not existpkg/executor/simple.go#L665-L667
悲观事务需tidb_constraint_check_in_place_pessimistic开启savepoint is not supported in pessimistic transactions when in-place constraint check is disabledpkg/executor/simple.go#L668-L670
同名重复执行SAVEPOINT不报错:旧记录先删除,再在新位置压入检查点pkg/sessionctx/variable/session.go#L504-L515

语法与用法要点

保存点名统一转小写后存储,s1S1指向同一记录,不存在 MySQL 中大小写敏感的坑(pkg/sessionctx/variable/session.go#L506):

SAVEPOINT s1; ROLLBACK TO SAVEPOINT s1; -- 与 ROLLBACK TO s1 等价 RELEASE SAVEPOINT s1;

ROLLBACK TO找到保存点后执行 MemDB 检查点回滚,事务不终止(pkg/executor/simple.go#L802-L810)。

完整流程拆解

步骤一:建表并开启悲观事务

目的:准备数据并进入显式事务,使保存点真正生效。

DROP TABLE IF EXISTS t_batch; CREATE TABLE t_batch(id int, val int, UNIQUE INDEX idx(id)); BEGIN PESSIMISTIC; INSERT INTO t_batch VALUES (1, 1), (3, 3);

该步验证的边界:BEGIN之前的SAVEPOINT是空操作,必须在事务内执行。

步骤二:打保存点后写入"问题数据"

目的:把检查点插在坏数据之前,为回滚留出分界。

SAVEPOINT s1; INSERT INTO t_batch VALUES (4, 4); SELECT * FROM t_batch ORDER BY id;

预期输出(示例结果):

id val 1 1 3 3 4 4

该步验证的边界:事务内查询能看到未提交写入,说明检查点记录在 MemDB 上而非落盘数据。

步骤三:回滚到保存点

目的:撤销 s1 之后的 (4,4),保留 (1,1)、(3,3)。

ROLLBACK TO s1; SELECT * FROM t_batch ORDER BY id;

预期输出(示例结果):

id val 1 1 3 3

该步验证的边界:回滚只撤销检查点之后的修改,事务仍处于活跃状态。

步骤四:继续写入并提交

目的:证明回滚后事务可继续写入并正常提交。

INSERT INTO t_batch VALUES (4, 40); COMMIT; SELECT * FROM t_batch ORDER BY id;

预期输出(示例结果):

id val 1 1 3 3 4 40

该步验证的边界:落盘数据只包含"最后一次回滚点之前加其后新写入"的行,(4,4) 不出现。

容易误判的语义

  • 栈式删除ROLLBACK TO s1会丢弃 s1 之后的全部保存点,且RELEASE SAVEPOINT同样删除目标之后的所有保存点(pkg/sessionctx/variable/session.go#L529-L548)。后果:依次建 s1、s2、s3 后回退到 s2,再执行ROLLBACK TO s3直接报[executor:1305]SAVEPOINT s3 does not exist,脚本若按"栈还能恢复"的假设编写会意外中断。
  • 非事务中静默忽略。autocommit 开启且不在事务中时,SAVEPOINT直接返回成功,不建任何记录(pkg/executor/simple.go#L665-L667)。后果:后续ROLLBACK TO报保存点不存在,排障时容易误判成"保存点丢失"。
  • 同名覆盖是位置替换。重复执行SAVEPOINT s1会先删除旧的 s1 再压入新记录,新检查点位于栈顶(pkg/sessionctx/variable/session.go#L504-L515)。后果:旧检查点连同它之后建立的保存点一并消失,回滚范围比预想更大。
  • 可重复回退。同一个保存点可以被多次ROLLBACK TO,每次只恢复到同一个 MemDB 检查点(pkg/executor/test/txn/txn_test.go#L381-L386)。后果:保存点可当作"重置到某个干净状态"的开关反复使用。
  • 名称大小写归一SAVEPOINT S1SAVEPOINT s1是同一记录(pkg/executor/test/txn/txn_test.go#L428-L431)。后果:与 MySQL 行为相反,依赖大小写区分两个保存点的写法在 TiDB 里会互相覆盖。

与 MySQL 的差异

  1. 锁的释放时机。MySQL 在ROLLBACK TO SAVEPOINT时释放保存点之后的行锁;TiDB 悲观事务不释放,统一在事务COMMIT或整体ROLLBACK时才释放(2022-07-22-transaction-savepoint.md MySQL Compatibility 节)。实际后果:回退保存点后,其他会话对同一批行的FOR UPDATE仍会阻塞到本事务结束,并发批处理任务可能长时间等待。
  2. 自增值不回收ROLLBACK TO SAVEPOINT不回收已分配的 AUTO_INCREMENT / SEQUENCE 值。实际后果:频繁部分回滚的表在提交后键值序列出现空洞,依赖"键值连续"做分片或对账的逻辑会读到空档。

如何验证操作生效

  1. 事务内执行SELECT对比回滚前后的行数,确认目标行已被撤销(参考 tests/integrationtest/r/executor/executor_txn.result)。
  2. COMMIT后再次SELECT,确认落盘数据只含保存点之前与最后一次回滚后新写入的行。
  3. 执行RELEASE SAVEPOINT试探:报[executor:1305]SAVEPOINT ... does not exist说明该保存点已不存在。

保存点栈本身无法用 SQL 查询,information_schema中没有对应视图,只能靠上述回退/释放是否报错来间接判断。

限制速查

限制表现来源
开启 binlog 时不可用SAVEPOINT is not supported when binlog is enabledpkg/executor/simple.go#L659-L660、2022-07-22-transaction-savepoint.md Savepoint Implementation 节
悲观事务需 in-place constraint check关闭时报savepoint is not supported in pessimistic transactions when in-place constraint check is disabledpkg/executor/simple.go#L668-L670
非事务 + autocommit 为空操作静默忽略,后续ROLLBACK TO[executor:1305]pkg/executor/simple.go#L665-L667、pkg/executor/test/txn/txn_test.go#L273-L274
不回滚 AUTO_INCREMENT / SEQUENCE键值出现空洞2022-07-22-transaction-savepoint.md ROLLBACK TO SAVEPOINT Implementation 节
悲观锁不随回退释放其他会话FOR UPDATE等待至事务结束2022-07-22-transaction-savepoint.md MySQL Compatibility 节

【免费下载链接】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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 4:36:41

机器学习股价预测源码解析:特征工程、模型选型与回测防过拟合

简介:一套基于机器学习算法的股票价格分析与预测源码包,面向计算机、人工智能、大数据、数学、电子信息等专业正在进行课程设计、期末大作业或毕业设计的学生,也适合对金融量化方向感兴趣、具备一定Python基础的学习者对照调试与二次开发。压…

作者头像 李华
网站建设 2026/9/13 4:34:27

电子洁净库房温湿度均一性监控与WiFi网格化布点实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 4:34:24

自建多模型对话后端:统一API、流式响应与适配器实战

简介:面向有一定Java与全栈基础的开发者,基于AI大模型API的自建后端对话服务项目ChatMASTER,可解决多模型统一接入、同步/流式响应(含打字机式输出效果)与私有化部署等问题。项目支持DeepSeek、月之暗面(Ki…

作者头像 李华
网站建设 2026/9/13 4:32:36

用 gs-quant 算出因子还能撑多久:IC 半衰期实战指南

用 gs-quant 算出因子还能撑多久:IC 半衰期实战指南 【免费下载链接】gs-quant Python toolkit for quantitative finance 项目地址: https://gitcode.com/GitHub_Trending/gs/gs-quant 上周组里刚评完一批候选因子,紧接着就有人追问:…

作者头像 李华
网站建设 2026/9/13 4:32:12

AI辅助专业工作:技术边界与真实价值解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华