MST

星途 面试题库

面试题:MySQL的行锁在高并发场景下如何保障数据一致性且避免死锁?

在高并发读写操作频繁的MySQL数据库场景中,行锁机制对保障数据一致性至关重要。请详细说明行锁在这种场景下是怎样实现数据一致性的维护,同时又采取了哪些策略来避免死锁问题,若发生死锁,MySQL内部是如何检测和处理的?
35.4万 热度难度
数据库MySQL

知识考点

AI 面试

面试题答案

一键面试

行锁实现数据一致性维护

  1. 写操作
    • 当一个事务对某一行数据进行写操作(如UPDATE、DELETE)时,会获取该行的排他锁(X锁)。此锁阻止其他事务对该行进行读写操作,确保在当前事务完成写操作前,其他事务无法修改该行数据,从而保证了数据的一致性。例如,一个银行转账事务,从账户A向账户B转账,在更新账户A和账户B余额这两行数据时,分别获取对应行的排他锁,防止其他事务同时修改这两个账户余额,避免出现数据不一致情况,比如重复转账或余额计算错误。
    • 对于INSERT操作,如果插入的行不存在,MySQL会自动获取该行的排他锁,确保新插入的数据不会与其他事务插入的数据产生冲突,保证插入操作的原子性和数据一致性。
  2. 读操作
    • 当一个事务对某一行数据进行读操作(如SELECT)时,默认情况下会获取该行的共享锁(S锁)。共享锁允许其他事务同时获取该行的共享锁进行读操作,但阻止其他事务获取排他锁进行写操作。这保证了在多个事务同时读取数据时,数据状态不会被其他写事务改变,从而保证了读取数据的一致性。例如,多个查询事务可以同时获取共享锁读取某一行商品库存数据,而不会相互干扰,且不会被修改库存的写事务影响读取结果。

避免死锁的策略

  1. 按相同顺序访问资源
    • 建议在事务中按照固定的顺序访问行数据。例如,在涉及多个账户的转账事务中,总是按照账户ID从小到大的顺序获取锁。这样可以避免不同事务以不同顺序获取锁而导致死锁。假设事务T1和T2都涉及账户A和账户B的操作,如果T1先获取A的锁再获取B的锁,T2也按照相同顺序获取锁,就不会出现死锁。
  2. 锁超时机制
    • MySQL设置了锁等待超时时间(innodb_lock_wait_timeout参数)。当一个事务请求锁的时间超过这个设定值时,该事务会自动回滚。这可以防止事务无限期等待锁,从而避免死锁情况。例如,若设置超时时间为50秒,一个事务等待某行锁超过50秒后,会自动回滚,释放已持有的锁,让其他事务有机会获取锁继续执行。
  3. 优化事务逻辑
    • 尽量减少事务持有锁的时间,将大事务拆分成小事务。小事务持有锁的时间短,降低了死锁发生的概率。比如,原本一个长事务包含多个复杂的数据库操作,持有锁时间较长,可将其拆分成几个简单的小事务,每个小事务完成一部分操作,快速释放锁,减少锁竞争。

MySQL内部死锁检测与处理

  1. 死锁检测
    • InnoDB存储引擎使用等待图(Wait - for Graph)算法来检测死锁。InnoDB维护一个锁信息的图结构,图中的节点是事务,边表示事务之间的锁等待关系。当一个事务请求锁时,如果无法立即获取锁,就会在等待图中添加一条从该事务到持有锁事务的边。InnoDB定期检查这个等待图,若发现有环,则表明存在死锁。例如,事务T1等待事务T2持有的锁,事务T2又等待事务T3持有的锁,而事务T3等待事务T1持有的锁,这样就形成了一个环,即检测到死锁。
  2. 死锁处理
    • 一旦检测到死锁,MySQL会选择一个牺牲者(Victim)事务进行回滚。通常选择回滚代价最小的事务,回滚代价主要考虑事务已执行的操作数量、事务持有锁的数量等因素。例如,一个只执行了少量操作且持有较少锁的事务会被优先选择作为牺牲者。被回滚的事务会释放其持有的所有锁,让其他事务可以继续执行,从而打破死锁状态。