数据库死锁是指在多个并发事务中,彼此之间发生相互等待的情况,导致所有事务都无法继续执行的情形。
关于死锁的概念之前有提到过,可参考文章:
对线面试官 - MySQL 隔离级别 、锁机制
数据库死锁通常由以下原因导致:
- 资源竞争:多个事务试图同时访问相同的资源,如数据库表、行、页或锁,但它们请求资源的顺序不同,导致相互等待。
- 未释放资源:事务在使用完资源后未及时释放,导致其他事务无法获得所需的资源。这可能是由于程序错误或异常情况引起的。
- 不同事务执行速度不同:某些事务执行速度较慢,持有资源的时间过长,其他事务需要等待释放,可能导致死锁。
- 操作数据量过大:事务在持有锁的同时,又请求获取更多的锁,导致互相等待。
解决(避免)死锁的方法包括:
- 减少锁的数量:使用更低级别的隔离级别如读提交(Read Committed),而非重复读(Repeatable Read),可以避免特定类型的锁竞争。
- 缩短事务持有锁的时间:优化事务处理逻辑,减少事务执行时间,降低发生死锁的可能性。
- 确定访问数据的固定顺序:在访问多个资源时,保持一致的访问顺序,以减少死锁的发生。
- 降低操作数据的量:减少事务需要操作的数据量,尽可能缩短事务的持有时间,以减少死锁的风险。
这些方法可以有效预防和解决数据库死锁问题,提升系统的并发处理能力和稳定性。
MySQL 只操作同一条记录,也会发生死锁吗?
答案是肯定会的。
因为数据库的锁机制针对的是索引而非记录本身。
在事务中,当我们更新一条记录时,如果使用普通索引作为条件,数据库会先获取普通索引的锁,然后尝试获取主键索引的锁。
若此时有另一个线程已经获得了该记录的主键索引锁,并且同时在其事务中试图获取该记录的普通索引锁,就可能导致死锁的发生。
代码语言:sql复制update my_table set name = 'paidaxing',age = 22 where name = "paidaxingwang";
这个SQL会先对name加锁, 然后再回表对id加锁。
-----
select * from my_table where id = 15 for update;
update my_table set age = 33 where name like "paidaxing%";
-- 以上SQL,会先获取主键的锁,然后再获取name的锁。
为了预防这种死锁情况,可以在应用程序中设定特定的索引获取顺序规则,比如规定只能按照主键索引 -> 普通索引的顺序获取锁。这样可以确保不同线程在获取锁时遵循统一的顺序,从而有效地避免死锁的发生(通过 SQL 保证)。
什么是死锁,如何解决?
死锁是指两个或两个以上的进程(或线程)在执行过程中,由于竞争资源或者彼此通信而造成的一种阻塞现象。在无外力作用下,它们都无法继续向前推进。这种状态被称为系统处于死锁状态,或者简称系统发生了死锁。这些相互等待的进程被称为死锁进程。
比如,丈母娘要求先买房才能结婚,但女婿坚持要先结婚再买房,这种情况类比了死锁的概念。
发生死锁的四个必要条件是:
- 互斥条件:一个资源每次只能被一个进程或线程使用。
- 占有且等待:一个进程因请求资源而阻塞时,继续持有已获得的资源。
- 不可抢占:已获得的资源在未使用完之前不可被强行剥夺。
- 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
解除死锁可以从以下几个方面入手:
- 破坏不可抢占条件:设置资源的优先级,允许高优先级的进程可以抢占低优先级进程的资源。
- 破坏循环等待条件:保证所有进程(线程)请求资源的顺序是一致的,比如按照固定的顺序请求资源,例如 A->B->C,避免形成循环等待。
在数据库中,如果多个事务并发执行,也可能会发生死锁。例如,当事务 1 持有资源 A 的锁,尝试获取资源 B 的锁,同时事务 2 持有资源 B 的锁,尝试获取资源 A 的锁时,就可能导致死锁的发生。
发生死锁时,可能会出现如下异常情况:
代码语言:plsql复制Error updating database. Cause: ERR-CODE: [TDDL-4614][ERR_EXECUTE_ON_MYSQL]
Deadlock found when trying to get lock;
一般来说,对于数据库的死锁问题,主要是要避免并发修改的冲突。另外一种方法是保证操作的顺序,例如多个事务都先操作资源 A,再操作资源 B,这样可以有效地避免死锁的发生。
如何排查死锁问题?您在生产环境中是否遇到过?逐步的排查方法是什么?感兴趣的小伙伴可以点赞收藏,下期出。
好了,本章节到此告一段落。希望对你有所帮助,祝学习顺利。
我正在参与2024腾讯技术创作特训营最新征文,快来和我瓜分大奖!