2026-10-23 07:36:21
MySQL唯一索引导致死锁的原因主要与并发事务执行INSERT ... ON DUPLICATE KEY UPDATE语句时,对同一行记录的加锁顺序不一致有关。以下是详细解释:
1. 唯一索引与死锁的关系
唯一索引确保数据的唯一性,但在并发环境下,多个事务可能同时尝试插入或更新相同唯一键值的记录。当使用INSERT ... ON DUPLICATE KEY UPDATE语句时,MySQL会先尝试插入,如果发现唯一键冲突,则转为更新操作。这个过程中涉及到的加锁机制可能导致死锁。
2. 死锁产生的具体场景
假设有两个事务T1和T2,并发执行以下操作:
-- T1INSERT INTO table (unique_key, other_column) VALUES ('key1', 'value1') ON DUPLICATE KEY UPDATE other_column = 'new_value1';-- T2INSERT INTO table (unique_key, other_column) VALUES ('key1', 'value2') ON DUPLICATE KEY UPDATE other_column = 'new_value2';3. 加锁顺序与死锁形成
T1执行过程:
尝试插入记录,获取该行的排他锁(X锁)。
如果发现唯一键冲突(如'key1'已存在),则转为更新操作。此时会先释放X锁,然后获取共享锁(S锁)读取现有记录。
根据读取到的记录,再次获取X锁进行更新。
T2执行过程:与T1类似,但在时间上与T1交错执行。
4. 死锁形成的关键点
当T1和T2以不同的顺序请求锁时,可能形成循环等待:
这样就形成了T1等待T2,T2又等待T1的循环等待局面,导致死锁。
5. 避免死锁的策略
6. 示例代码与解释
-- 创建带有唯一索引的表CREATE TABLE example ( id INT AUTO_INCREMENT PRIMARY KEY, unique_key VARCHAR(100) UNIQUE, other_column VARCHAR(100), version INT DEFAULT 0);-- 使用乐观锁避免死锁UPDATE example SET other_column = 'new_value', version = version + 1 WHERE unique_key = 'key1' AND version = 0;在这个乐观锁的例子中,通过版本号控制并发更新,避免了数据库锁的使用,从而减少了死锁的可能性。
7. 结论
唯一索引本身并不直接导致死锁,但在并发环境下,特别是使用INSERT ... ON DUPLICATE KEY UPDATE这类语句时,由于加锁机制的复杂性,可能导致死锁。理解死锁的形成原因,并采取适当的避免策略,是确保数据库并发性能的关键。