必须关闭autocommit=1才能使事务生效;拆分需按业务边界(如主键范围、时间分区)而非行数硬切;须配合for update加锁防幻读,并依赖外键、唯一索引、check等约束保障每批操作的数据合法性。

大事务拆分前必须确认 autocommit 状态
MySQL 默认 autocommit=1,这意味着每条 INSERT/UPDATE/DELETE 都会自动提交——哪怕你写了 START TRANSACTION,也根本没生效。不关掉它,所谓“拆分”只是自我安慰。
执行前务必检查:SELECT @@autocommit;,返回 1 就得先运行 SET autocommit = 0;。否则后续所有 COMMIT/ROLLBACK 都无效,锁会一直挂着,undo log 持续膨胀,拖垮整个实例。
按数据边界分批提交,别按行数硬切
常见错误是写个循环,每 1000 行 COMMIT 一次。但若这 1000 行跨多个业务逻辑单元(比如用户订单+积分+优惠券),中间失败就无法原子回滚,一致性立刻崩坏。
正确做法是识别自然业务边界:
- 按主键范围切:比如
WHERE id BETWEEN 10001 AND 20000,确保单批次操作只影响一个逻辑实体集 - 按时间分区切:如处理「2026-07-01」当天的订单,避免跨日事务混杂状态
- 优先用
WHERE条件带索引字段,否则UPDATE扫描全表,拆了也卡
每次提交后,建议查 SELECT ROW_COUNT(); 确认实际影响行数,防止条件写错导致漏处理。
拆分后如何防幻读与中间态不一致
拆成小事务后,REPEATABLE READ 下仍可能幻读——比如你分批统计并更新用户积分,第二批次执行时,其他事务插入了新订单,导致总和不准。
关键动作不是调高隔离级别,而是主动加锁:
- 对要校验的范围数据,用
SELECT COUNT(*) FROM orders WHERE user_id = 123 AND created_at >= '2026-07-01' FOR UPDATE -
FOR UPDATE触发临键锁(Next-Key Lock),阻止其他事务在该范围内插入新行 - 注意:若
created_at字段没索引,会升级为表锁,直接堵死并发
别依赖“先查再改”的应用层判断——查完到改之间的时间窗就是不一致温床。
外键、唯一索引、CHECK 是一致性底线,不是可选项
事务拆分后,原子性范围缩小,约束机制反而更关键。没有它们,小事务只会放大问题:
-
FOREIGN KEY被禁用?子表插入时父记录还没落库,直接报错或存脏数据 - 余额字段没
CHECK (balance >= 0)?拆分过程中某次扣款成功但入账失败,账户就变负数 - 并发插入相同业务单号,没唯一索引?
INSERT IGNORE或ON DUPLICATE KEY UPDATE失效,生成重复单
这些约束必须在表结构里定义,不能只靠代码校验——应用层永远比数据库慢半拍,且无法覆盖所有接入点。
拆分本身不难,难的是每一批都得像单体事务一样守住数据合法边界。最容易被忽略的,是把“事务拆了”当成目标,却忘了每一块碎片仍需独立满足一致性条件。











