知识卡片

幻读导致写偏差的通用模式及物化冲突这个最后手段

普通读书笔记卡

内容

[[写偏差是比丢失更新更隐蔽的多对象写写冲突]]除了医生值班案例外,还广泛出现在会议室 预订(检查时间段是否已被占用再插入新预订)、多人游戏(棋子移动到同一位置)、用户名 抢注(检查用户名是否已用再创建账户)、防止双重开支(检查余额是否够再插入支出项) 这些场景,它们的共同模式是:先SELECT查出满足某条件的行并据此判断,再执行一次写入, 而这次写入恰好改变了SELECT查询本该看到的结果集——如果提交后重新执行同一个SELECT, 结果会不一样。这种”一个事务的写入改变了另一个事务搜索查询结果”的现象叫幻读:不同于 医生案例(写入修改的正是SELECT已经返回的那几行,锁住这些行就能防住),幻读案例的 写入是新增一行满足条件的记录、而SELECT当时可能根本没返回任何行可锁——SELECT FOR UPDATE在这种”没查到东西”的场景下压根锁不住任何对象。一个笨拙但可行的应对方法叫 物化冲突:人为在数据库里造出一个可以被锁定的对象来代表这个冲突域,比如把会议室 预订按房间和15分钟时间槽预先枚举成一张锁表,创建预订前先锁住对应的房间时间槽行, 这样幻读就被转化成了普通的行级锁冲突。但这个方法要求你能想清楚怎样把抽象的业务 约束”物化”成具体可锁的行,操作起来困难且容易出错,还把并发控制的细节泄漏进了业务 数据模型,因此原文明确说:物化冲突应被视为最后的手段,绝大多数情况下真正的可串行化 隔离级别是更可取的选择。

参考来源

- 位置:《数据密集型应用系统设计》第七章《事务》"写偏差的更多例子""导致写入偏差的 幻读""物化冲突"(源文件:_epub-src/ch7_split_003.html, ch7_split_004.html) - 结论依据:原文列出会议室预订/多人游戏/用户名抢注/防止双重开支多个写偏差案例, 归纳出"SELECT检查条件→写入改变该条件"的幻读通用模式,并说明物化冲突通过人为 制造可锁对象规避幻读,但应被视为最后手段,直接支撑本卡片结论。 - 原始内容:这个写入的效果改变了步骤2中的先决条件……这种效应:一个事务中的写入 改变另一个事务的搜索查询的结果,被称为幻读……这种方法被称为物化冲突……出于 这些原因,如果没有其他办法可以实现,物化冲突应被视为最后的手段。