避免MySQL批量更新形成超大事务,核心是单次更新不超过1000行;WHERE必须走索引,禁用隐式转换与字段函数;用主键范围分片替代LIMIT OFFSET;每批显式COMMIT并适当休眠;多表更新按固定顺序加锁;纯DML操作,禁止混入非DB逻辑。

避免MySQL批量更新形成超大事务,核心就一条:不许让单个UPDATE语句或单个事务处理超过1000行数据。 超过这个量级,undo log、锁持有时间、binlog写入压力、主从延迟全会陡增——这不是性能问题,是服务稳定性红线。
WHERE条件必须走索引,否则分批也白搭
没索引的WHERE条件,哪怕加了LIMIT 100,MySQL仍可能全表扫描并给所有扫描行加锁。你看到的是“只更新100行”,实际锁住了上百万行。
- 用
EXPLAIN UPDATE ...检查key列是否非NULL,type不能是ALL - 避免隐式转换:
status = 1(字段是VARCHAR)→ 改成status = '1' - 别在字段上套函数:
DATE(create_time) = '2026-07-28'→ 改成create_time >= '2026-07-28' AND create_time - 复合查询注意最左前缀:查
status = 'pending' AND created_at > '2023-01-01',索引得是(status, created_at),不是单列created_at
用主键范围分片,别信LIMIT OFFSET
LIMIT 1000 OFFSET 10000看着简单,但MySQL每次都要跳过前10000行,越往后越慢,还可能漏数据或重复更新。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 正确做法:先查
SELECT id FROM t WHERE status = 0 ORDER BY id LIMIT 1000,拿到这批最大id(比如100999) - 下一批用
WHERE id > 100999 AND status = 0 ORDER BY id LIMIT 1000 - 每批必须显式
COMMIT,不能依赖ORM自动提交 - 每批后加
SLEEP(0.01)或SLEEP(0.1),缓解锁队列堆积和主从复制压力
多表更新必须统一加锁顺序
两个事务分别按user → order和order → user更新,死锁是确定性事件,不是概率问题。
- 所有业务代码约定唯一顺序:按表名字母序(
order_item → order → user),或按业务流向(order → order_item → payment) - 禁止根据
type == 'refund'动态拼SQL顺序 - 每张表的更新都必须基于主键或唯一索引,避免
SELECT FOR UPDATE升级为宽范围间隙锁 - 连接池中每次分批前执行
RESET CONNECTION,防止复用未清理的事务上下文
最容易被忽略的点是:事务里混了非DB操作——比如远程调用、缓存更新、文件IO。这些会让事务存活时间远超预期,锁根本没机会释放。批量更新必须是纯DML操作,其他逻辑全部挪到事务外。

















