面试题答案
一键面试潜在一致性问题
- 写偏序异常(Write Skew)
- 问题描述:在快照隔离级别下,两个事务可能同时读取到相同的数据快照,然后各自基于这个快照进行不同的修改,且都认为自己的修改是有效的,从而导致违反业务逻辑的一致性。例如,在一个银行转账场景中,事务A从账户X向账户Y转账,事务B从账户Y向账户X转账,两个事务同时进行,由于它们读取的是相同的快照,可能都成功提交,导致账户余额未按预期变化。
- 原因:快照隔离级别下,每个事务读取的是一个时间点的快照数据,事务之间的写操作没有正确的顺序控制。
- 丢失更新(Lost Update)
- 问题描述:两个事务同时读取同一数据行,然后各自对其进行修改并提交。后提交的事务会覆盖先提交事务的修改,从而导致先提交事务的更新丢失。例如,在一个库存管理系统中,两个事务同时读取某商品的库存数量,一个事务增加库存,另一个事务减少库存,最后提交的事务会决定最终的库存值,而另一个事务的操作效果被忽略。
- 原因:快照隔离没有阻止并发事务对相同数据进行修改,缺乏对更新操作的有效协调。
可能的解决方案
- 针对写偏序异常
- 使用序列化隔离级别:这是PostgreSQL中最强的隔离级别,它通过强制事务串行执行来避免所有并发问题,包括写偏序异常。但缺点是性能较低,因为事务不能并发执行。
- 应用层检测和重试:在应用程序层面,事务提交前检测是否违反业务逻辑。如果发现可能的写偏序异常,回滚事务并重新执行。例如,在银行转账场景中,转账完成后检查账户余额是否符合预期逻辑,如果不符合则回滚并重试。
- 针对丢失更新
- 使用行级锁:在读取数据时,使用SELECT... FOR UPDATE语句获取行级排他锁。这样在一个事务读取并锁定数据行后,其他事务无法同时读取和修改该行数据,直到该事务提交或回滚。例如,在库存管理场景中,事务在读取库存数据时就锁定该行,确保只有一个事务能修改库存。
- 使用乐观锁:在数据行中添加一个版本号字段。事务读取数据时获取版本号,在更新数据时,将当前版本号与数据库中的版本号进行比较。如果版本号一致,则更新数据并递增版本号;如果不一致,则说明数据已被其他事务修改,回滚当前事务并重新执行。