MySQL死锁
Posted dddjp
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL死锁相关的知识,希望对你有一定的参考价值。
死锁产生的条件:
(1)互斥条件:一个资源在同一时刻只能被一个进程占有
(2)请求与保持:一个进程因请求资源发生阻塞时,对当前已占有的资源不释放
(3)不可剥夺:对于进程已获得的资源,在使用完成之前,不允许其他进程剥夺
(4)循环等待:循环等待资源的环形状态
myisam是deadlock free的,myisam要么一次性获得全部的锁,要么等待,不会产生死锁。
innodb的锁是逐步获得的,如果两个事务在执行过程中,都需要等待对方持有的排他锁,此时,就会产生死锁
innodb存储引擎有自动探测死锁的机制
1.innodb自动检测死锁
(1)产生死锁之后,innodb回很快检测到,并回滚一个代价较小的事务,通过计算事务插入的行等,判断事务的大小
(2)InnoDB_lock_wait_timeout参数,当等待的时间超过该值时,自动回滚
注意:该参数不仅可以解决死锁问题,当并发访问量较高时,避免大量事务因获取不到锁尔挂起,造成数据库资源浪费
(3)wait for graph 算法
该算法将事务看成节点,资源就是各个事务占用的锁,当事务1需要等待事务2的锁时,就生成一条有向边从1指向2,最后行成一个有向图。
如果该有向图有环,意味着死锁,每当加锁请求无法立即满足需要并进入等待时,wait-for graph算法都会被触发。
2.死锁的示例
(1)两个事务操作不同表的相同的记录行
解决:在必须锁定多个资源的情况下,约定不同事务中,必须按照相同的顺序锁定资源。
事务A | 事务B |
update user set name = ‘duan‘ where id = 1 (id是主键,给该行加排他锁)
delete from order where id = 1 (想要再给当前行加排他锁时,发现事务B已在当前行上加了排他锁,阻塞)
|
delete from order where id = 1 (id是主键,给该行加排他锁)
update user set name = ‘duan‘ where id = 1 (想要再给当前行加排他锁时,发现事务A已在当前行上加了排他锁,阻塞) |
(2)
update的执行过程分两步,读取当前记录,更新当前记录
解决:更新锁
当执行update时,数据库系统给当前记录加上一个更新锁(更新锁和共享锁可以兼容,但是,一个资源最多只能拥有一把更新锁)
在读取数据完毕之后,将更新锁上升为排他锁
由于一个资源只能拥有一把更新锁,所以,只能一个事务结束之后, 另一个事务开始执行
事务A | 事务B |
select * from user where id = 2; (给当前记录将更新锁) |
|
update user set name = ‘duan‘ where id = 2; (事务B企图在ID=2的行上加排他锁,但是,该记录已经有共享锁,由于排他锁不能和任何锁兼容,此时,inndb会加上一个意向排他锁) |
|
update user set name = ‘duanjiaping‘ where id = 2; (事务A此时企图将加在ID=2的记录上的共享锁上升为排他锁,但是,发现该记录上有事务B加上的意向排他锁,一个记录上只能有一个意向排他锁,因此,阻塞) |
|
(3)
事务A | 事务B |
update user set name = ‘d‘ where id =1 | |
update user set name = ‘d‘ where id =2 | |
update user set name = ‘j‘ where id = 2 | |
update user set name = ‘j‘ where id = 1 |
(4)
可以使用不同的索引锁定不同的行
不能使用不同的索引锁定相同的行
事务A | 事务B |
update user set name = ‘d‘ where id = 3 | |
update set name =‘d‘ where stuNo = 2 (stuNo列也建立了索引,且与事务A锁定的是统一记录) |
(5)
执行update时,如果where筛选条件制定的记录存在,则,只会锁定当前行,否则,行锁上升为表锁
以上是关于MySQL死锁的主要内容,如果未能解决你的问题,请参考以下文章