写偏斜:RR 挡不住,SERIALIZABLE 靠死锁挡住
规则是至少一个医生值班,现在 alice 和 bob 都在岗。两人同时请假:
-- alice 和 bob 各自执行
BEGIN;
SELECT SUM(on_call) FROM duty; -- 都读到 2
UPDATE duty SET on_call = 0 WHERE name = '自己';
COMMIT;我实际跑了三种做法:
| 做法 | 结果 |
|---|---|
| RR | 两人都成功,最后 0 人值班 |
| SERIALIZABLE | bob 报死锁 1213 被回滚,最后 1 人值班 |
RR + SELECT ... FOR UPDATE | bob 在读的时候就等,alice 提交后 bob 读到 1 |
RR 下两人改的是不同的行,行锁不冲突,这就是写偏斜。
SERIALIZABLE 把普通 SELECT 变成 FOR SHARE:两人都拿到对方那行的共享锁,再各自要排他锁,互相等成死锁,InnoDB 回滚一个。它不是让人排队,而是把写偏斜变成死锁再杀掉一个,所以应用要会重试。
实际更推荐第三种:在要做判断的读上直接 FOR UPDATE,第二个人在读的时候就排队,不会死锁。