摘要:死锁在开发中经常遇见,下面我就来重点详解死锁的原因及解决方法,主要分为如下4大点@mikechen
死锁在开发中经常遇见,下面我就来重点详解死锁的原因及解决方法,主要分为如下4大点@mikechen
作者:mikechen
死锁:是指在并发系统中,两个或多个事务(或线程、进程…),因为互相持有对方需要的资源。
并且都在等待对方释放自己持有的资源,从而导致所有事务都无法继续执行下去的僵局状态,这就是死锁。
如下图所示:
假设有两个线程: T1 和 T2:
T1 先锁住 资源 A,然后尝试获取 资源 B;T2 先锁住 资源 B,然后尝试获取 资源 A;此时,T1 在等待 T2 释放 资源 B,T2 在等待 T1 释放 资源 A,双方都无法继续执行,就发生了死锁。
再比如:现实生活中最经典的例子,就是两辆车互相让道。
想象一下两条狭窄的单行道,两辆车分别从相反的方向驶来。
如果它们在中间相遇,并且都不愿意后退,那么这两辆车就陷入了死锁状态,它们都无法前进。
死锁产生原因
死锁发生,满足以下条件:
1.互斥条件
资源同一时间,只能被一个进程独占(如打印机、数据库行锁)。
例如,数据库中的一行记录被一个事务加了排他锁,其他事务就不能再对该行加排他锁或共享锁。
2.持有并等待
进程已持有资源,同时请求新资源。
比如:事务在等待新资源的同时,不释放已占有的资源。
3.不可剥夺
已分配资源,不能被强制回收。
4.循环等待
存在进程间的环形等待链(如A等B,B等C,C等A)。
例如,事务 A 持有资源 X 并等待资源 Y,事务 B 持有资源 Y 并等待资源 X,形成了一个 A → 等待 Y ← B → 等待 X ← A 的环路。
如何解决死锁问题
破坏死锁的四个必要条件之一,使死锁不可能发生。
加锁顺序统一
所有事务,按照相同的顺序申请资源,避免循环…等待。
比如:
-- 事务 ABEGIN;lock TABLE table_A WRITE;LOCK TABLE table_B WRITE; -- 等待 table_B 释放COMMIT; -- 事务 BBEGIN;LOCK TABLE table_B WRITE;LOCK TABLE table_A WRITE; -- 等待 table_A 释放COMMIT;事务 A:先锁 表 A,再锁 表 B。
事务 B:先锁 表 B,再锁 表 A(可能形成死锁)。
修改为:
-- 事务 A 和事务 B 都按 table_A → table_B 顺序加锁BEGIN;LOCK TABLE table_A WRITE;LOCK TABLE table_B WRITE;COMMIT;所有事务都先锁 table_A,再锁 table_B,统一加锁顺序!
设置超时时间
SET innodb_lock_wait_timeout = 5; 避免事务长时间等待锁释放。
分批次更新数据
一次更新太多行容易造成锁争用,拆成小批量更新减少锁冲突。
比如: orders 表有 100 万条数据,这个事务可能锁住大量行,导致死锁!
UPDATE orders SET status = 'shipped' WHERE status = 'pending';使用 LIMIT 分批更新,比如:
UPDATE orders SET status = 'shipped' WHERE status = 'pending' LIMIT 1000;来源:弘bai瑞教育