数据库死锁的解决方式:

死锁是指多个事务在执行时,因争夺锁资源而造成的一种互相等待的现象。解决方法有:

  • 超时机制: 通过参数 Innodb_lock_wait_timeout 来设置超时等待时间,两个事务互相等待时,达到设置的超时等待时间后,其中一个事务进行回滚,这样另一个等待的事务就能继续进行了。优点是简单,缺点是如果事务操作更新了很多行,那么进行回滚会非常占用时间。
  • 等待图: InnoDB 引擎采用的是等待图的方式来进行死锁检测,它保存锁的信息链表和事务等待链表,采用深度优先算法来判断是否存在回路,如果存在,就表示存在死锁,通过将持有最少行级排他锁的事务进行回滚来解决。(回滚 undo 量最小的事务)

什么是死锁

死锁是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,如果没有外力作用,它们都将无法继续向前推进。 比如有一个线程A,按照先锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁。此时线程a持有第一个锁,等待获取第二个锁,而线程b持有第二个锁,等待获取第一个锁,这样就造成了死锁。

死锁的必要条件

  • 互斥条件:每个资源要么已经分配给了一个进程,要么就是可用的。
  • 请求和保持条件:已经得到了某个资源的进程可以再请求新的资源,并且对已获得的资源保持不放。
  • 不可抢占条件:进程获取到的资源在使用完之前不能被其他线程抢占。
  • 环路等待条件:有两个或者两个以上的进程组成一条环路,该环路中的每个进程都在等待下一个进程所占有的资源。破坏方法:给资源统一编号,进程只能按编号顺序来请求资源。

解决方式

  • 死锁预防: 破坏死锁的必要条件——破坏请求和保持条件:让所有进程在执行前请求所需要的全部资源。破坏环路等待条件:给资源统一编号,进程只能按编号顺序来请求资源。
  • 死锁检测与死锁恢复: 不试图阻止死锁,而是当检测到死锁发生时,采取措施进行恢复,比如抢占恢复、回滚恢复、通过杀死进程来恢复。
  • 死锁避免: 在程序运行时避免发生死锁。(银行家算法)
  • (不推荐)鸵鸟策略: 也就是假装没发生问题,因为解决死锁的代价很高,所以鸵鸟策略会获得很高的性能。在死锁不会对用户造成多大影响,或者发生概率很低时,可以采用鸵鸟策略。

死锁场景

  • 使用 ReentranLock 时未通过 unlock 释放锁。

数据库中的死锁

sql server数据库死锁了 数据库死锁解决_死锁

手写死锁

public class deadLock {
    private static final Object A = new Object();
    private static final Object B = new Object();

    public static void main(String[] args) {
        Thread threadA = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (A) {
                    System.out.println(Thread.currentThread() + "get sourceA");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get sourceB");
                    synchronized (B) {
                        System.out.println(Thread.currentThread() + "get sourceB");
                    }
                }
            }
        });

        Thread threadB = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (B) {
                    System.out.println(Thread.currentThread() + "get sourceB");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread() + "waiting get sourceA");
                    synchronized (A) {
                        System.out.println(Thread.currentThread() + "get sourceA");
                    }
                }
            }
        });

        threadA.start();
        threadB.start();
    }
}