如果有遗漏,评论区告诉我进行补充
面试官: 数据库中的 什么是死锁?如何解决死锁?什么是乐观锁和悲观锁?
我回答:
什么是死锁?
死锁是指两个或多个事务在执行过程中,因争夺资源而造成的一种相互等待的现象,若无外力作用,这些事务都将无法向前推进。在数据库中,死锁通常发生在多个事务尝试以不同的顺序访问相同的数据资源时,每个事务都持有对方需要的资源的一部分,并等待对方释放资源。
死锁的特点:
- 互斥条件:资源不能被多个事务共享,只能被一个事务使用。
- 请求与保持条件:事务已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他事务占用。
- 不剥夺条件:事务已获得的资源在未使用完之前,不能被其他事务强行夺走。
- 循环等待条件:多个事务之间形成一种头尾相接的循环等待资源的关系。
如何解决死锁?
- 超时:设置事务的最长执行时间,超过这个时间就强制回滚。
- 优先级:给每个事务分配一个优先级,当发生资源冲突时,优先级高的事务可以抢占低优先级事务的资源。
- 死锁检测:数据库管理系统定期检测是否存在死锁,并采取措施解除。
- 死锁预防:通过锁定策略或锁定顺序来防止死锁的发生。
- 最小化锁定范围:尽可能减少锁定的范围和时间。
-
预防死锁:
- 破坏互斥条件:一般不适用,因为很多资源是互斥的。
- 破坏请求与保持条件:要求事务一次性申请所有需要的资源,若不能同时满足则等待。
- 破坏不剥夺条件:当一个事务持有的资源被其他事务请求时,可强制释放部分或全部资源。
- 破坏循环等待条件:给资源编号,事务申请资源时按编号顺序申请。
-
避免死锁:
- 银行家算法:一种避免死锁的著名算法,通过预先分析资源分配的安全性来避免死锁。
-
检测与恢复:
- 周期性地检查系统是否发生死锁,发现后采取相应措施,如终止一个或多个事务,释放资源。
乐观锁 vs 悲观锁
乐观锁
- 定义:乐观锁(Optimistic Locking)是一种假设数据在一段时间内不会被修改的锁定策略。它在更新数据时检查在此期间有没有其他事务更新过该数据。
- 实现方式:
- 版本号:在数据表中添加一个版本号字段,每次更新数据时,都会检查版本号是否发生变化。
- 时间戳:记录最后一次更新的时间戳,在更新时检查时间戳是否改变。
- 优点:减少了锁定资源的时间,提高了系统的并发能力。
- 缺点:如果并发更新同一数据的次数较多,可能会导致更新失败的概率增加。
悲观锁
- 定义:悲观锁(Pessimistic Locking)是一种假设数据会被其他事务修改的锁定策略。它在事务开始时就锁定数据,直到事务结束才会释放锁。
- 实现方式:
- 共享锁(S Lock):允许多个事务读取同一数据,但不允许写入。
- 排他锁(X Lock):只允许一个事务读取或写入数据。
- 优点:确保了数据的一致性,减少了数据冲突的机会。
- 缺点:增加了锁定资源的时间,降低了系统的并发能力。
举例说明
死锁示例
假设有两个事务 A 和 B,事务 A 已经获得了对资源 R1 的锁,并请求对资源 R2 的锁;事务 B 已经获得了对资源 R2 的锁,并请求对资源 R1 的锁。在这种情况下,事务 A 和 B 都在等待对方释放锁,从而形成死锁。
乐观锁示例
假设数据库表中有一个记录,包含一个版本号字段 version
。事务 A 读取了这条记录,此时版本号为 1。在事务 A 更新记录时,它会检查版本号是否仍然是 1。如果版本号变为 2(意味着在此期间有其他事务进行了更新),那么事务 A 的更新将失败,并提示版本冲突。
悲观锁示例
事务 A 在开始时就锁定了记录,其他事务在这段时间内无法对该记录进行任何操作。事务 A 完成更新后,释放锁,其他事务才能继续操作。
总结
- 死锁:两个或更多事务互相等待对方释放资源的情况。死锁是数据库并发控制中的一个重要问题,通过预防、避免、检测与恢复等策略可以解决。
- 乐观锁:假设数据在一段时间内不会被修改,更新时检查数据是否被修改。
- 悲观锁:假设数据会被其他事务修改,从一开始就锁定数据直到事务结束。
在实际应用中,选择使用乐观锁还是悲观锁取决于具体的应用场景和对数据一致性的要求。通常,对于读多写少的应用,可以采用乐观锁来提高并发性能;而对于写操作频繁的应用,则可能更适合使用悲观锁来保证数据的一致性。