死锁是指两个或两个以上的事务在执行过程中互相持有对方期待的锁,若没有其他机制,它们都将无法进行下去。
例如,事务1在表A上持有一个排它锁,同时试图请求一个在表B上的排它锁,而事务2已经持有表B的排它锁,同时却在请求A表上的一个排它锁,那么两个事务就都不能执行了。
PG能够自动侦测到死锁,然后退出其中一个事务,从而允许其他事务执行。不过,哪个事务会被退出是很难预测的。
死锁发生条件
死锁发生必须具备以下4个必要条件:
- 互斥条件:指事务对所分配到的资源加了排它锁,即在一段时间内只能由一个事务加锁占用。如果此时还有其他进程请求排它锁,则请求者只能等待,直至持有排它锁的事务释放排它锁
- 请求和保持条件:指事务已经至少持有了一把排它锁,但又提出了新的排它锁请求,而该资源上的排它锁已被其他事务占有,此时请求被阻塞,但同时它对自己已获得的排它锁又持有不放
- 不剥夺条件:指事务已获得的锁在未使用完之前不能被其他进程剥夺,只能在使用完时由自己释放
- 环路等待条件:指发生死锁时,必然存在一个事务-资源的环形链,即事务集合{T0,T1,T2…Tn}中的T0正在等待T1持有的排它锁;P1正在等待P2持有的排它锁,…,Pn正在等待已被P0持有的排它锁
死锁解决方案
- 防止死锁最好的方法通常是保证使用一个数据库的所有应用都以相同的顺序在多个对象上请求排它锁。比如,在应用编程中,认为规定在一个事务中只能以一个固定顺序来更新表。假设在数据库中有ABCD4张表,现规定只能按如下顺序修改这几张表:B->C->A->D,若某个进程先更新了A表,如果想更新C表,则必须先回滚先前对A表的更新,然后再按规定的顺序,先更新C表,再更新A表。
- 数据库可以自动检测出死锁,所以应用也可以通过捕获死锁异常来处理死锁。但这个方法并不是很好,因为数据库检测死锁需要付出一定的代价,可能会导致应用程序过久地持有排它锁而使系统的并发处理能力下降。
- 排它锁持有的事件越长就越容易导致死锁,所以在进行程序设计时要尽量短时间地持有排它锁。