serializable 不会导致 mysql 崩溃,而是通过自动加读锁和行锁+间隙锁引发事务排队超时;应按需会话级启用并缩短事务粒度,而非降级到 rc 手动加锁。

Serializable 会直接阻塞并发写,不是“崩溃”,而是事务排队等锁超时——根本不用降级到 RC 再手动加锁,那反而更难控、更容易出错。
为什么 SET TRANSACTION ISOLATION LEVEL SERIALIZABLE 不会导致“崩溃”
MySQL 的 SERIALIZABLE 隔离级别不会让数据库进程崩溃,它只是把所有普通 SELECT 自动转成 SELECT ... LOCK IN SHARE MODE(读锁),写操作仍走行锁+间隙锁。真实问题是:多个事务争同一行或同一范围时,后到的事务会卡在 Waiting for table metadata lock 或 Waiting for lock 状态,直到 lock_wait_timeout(默认 50 秒)触发 Lock wait timeout exceeded 错误。
- 这不是 MySQL 崩溃,是客户端收到超时异常(
ERROR 1205或ERROR 1213) - 日志里看不到 crash trace,只有大量
TRANSACTION ... WAITING FOR ...记录 - 真正压垮服务的,往往是应用层没捕获超时、反复重试、连接池耗尽
别用 RC + 手动加锁替代 SERIALIZABLE
有人想“降级到 READ COMMITTED,再对关键语句补 SELECT ... FOR UPDATE”,这看似灵活,实则危险:
-
READ COMMITTED下没有间隙锁,SELECT ... FOR UPDATE只锁命中行,不锁区间 → 幻读照常发生 - 漏加一把锁(比如忘了在 INSERT 前查重加锁),就可能引发脏写或超卖
- 业务逻辑分散在多条 SQL 中,加锁顺序不一致 → 死锁概率反升
- 开发/维护成本远高于直接用
SERIALIZABLE+ 缩小事务粒度
真正该做的:保 SERIALIZABLE,但收缩锁范围和时长
坚持用 SERIALIZABLE,但避免让它锁整张表或锁太久:
- 把大事务拆成多个小事务,例如:先
SELECT ... FOR UPDATE查库存并校验,再单独发扣减语句,中间不掺杂日志、HTTP 调用等耗时操作 - 确保 WHERE 条件能命中索引,否则
SERIALIZABLE会升级为表级锁(InnoDB 无法对无索引字段做间隙锁) - 用
SELECT ... FOR UPDATE NOWAIT替代默认阻塞,让应用立刻感知冲突,自行退避重试,而不是干等 50 秒 - 检查是否真需要全表串行——很多场景其实只需对某几行强一致,可用
SELECT ... FOR UPDATE显式加锁,其余部分保持REPEATABLE READ
最容易被忽略的一点:SERIALIZABLE 不等于“必须全局开启”
你不需要把整个数据库或所有会话设成 SERIALIZABLE。真正关键的是:哪几条 SQL 绝对不能容忍幻读?只对它们启用:
- 用
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE开启会话级隔离 - 紧接着执行那条核心查询(如
SELECT COUNT(*) FROM orders WHERE status = 'pending') - 立刻
COMMIT,锁释放,后续普通查询回归默认REPEATABLE READ
这种“按需升格”比全局降级更精准,也比在 RC 里手抖漏锁更可靠。











