面试题答案
一键面试不同锁机制相互作用导致死锁的情况
- 场景示例
假设有两个事务
T1
和T2
,操作两张表TableA
和TableB
。T1
首先获取TableA
的共享锁(S锁),因为可能只是读取数据。然后尝试获取TableB
的排他锁(X锁),用于修改TableB
数据。T2
先获取了TableB
的共享锁(S锁),之后试图获取TableA
的排他锁(X锁)。 由于T1
持有TableA
的S锁,阻止T2
获取TableA
的X锁;同时T2
持有TableB
的S锁,阻止T1
获取TableB
的X锁。双方都在等待对方释放锁,从而形成死锁。
- 原理分析 共享锁(S锁)允许多个事务同时读取数据,但阻止其他事务获取排他锁(X锁)。排他锁(X锁)则不允许其他事务获取任何锁,无论是共享锁还是排他锁。在复杂事务场景中,如果事务获取锁的顺序不一致,并且每个事务都持有部分锁同时等待其他事务持有的锁,就容易出现死锁。
针对复杂场景的优化思路和方法
- 优化锁获取顺序
确保所有事务以相同的顺序获取锁。例如,无论是哪个事务,都先获取
TableA
的锁,再获取TableB
的锁。这样可以避免因锁获取顺序不一致导致的死锁。 - 设置合理的锁超时时间
为事务设置合理的锁等待超时时间。当一个事务等待锁的时间超过设定值时,自动回滚该事务,释放其持有的锁,从而打破死锁。在MySQL中,可以通过设置
innodb_lock_wait_timeout
参数来调整锁等待超时时间。 - 使用死锁检测机制
MySQL自身具备死锁检测机制,当检测到死锁时,会自动选择一个事务回滚,释放其持有的锁,以打破死锁。可以适当调整死锁检测的相关参数,如
innodb_deadlock_detect
来优化死锁检测的性能。 - 减少锁的粒度 尽量使用行级锁而不是表级锁。行级锁只锁定需要操作的行,对其他行的并发访问影响较小,能降低死锁发生的概率。在MySQL中,InnoDB存储引擎默认使用行级锁,但在某些情况下(如全表扫描)可能会升级为表级锁,需要注意避免这种情况。
- 优化事务设计 尽量缩短事务的执行时间,减少锁的持有时间。例如,将大事务拆分成多个小事务,尽快释放锁,降低死锁风险。同时,在事务中避免不必要的锁获取操作,只在真正需要修改数据时获取排他锁。