时间:2021-07-15 | 标签: | 作者:Q8 | 来源:王起帆网络
小提示:您能找到这篇{腾讯云:这个不可思议的死锁你会解吗?}绝对不是偶然,我们能帮您找到潜在客户,解决您的困扰。如果您对本页介绍的腾讯云:这个不可思议的死锁你会解吗?内容感兴趣,有相关需求意向欢迎拨打我们的服务热线,或留言咨询,我们将第一时间联系您! |
我们都知道,数据库系统中,不同线程并发访问数据,为了保护数据,在执行SQL语句时候需要对数据加锁。而死锁是一个经常遇到问题,SQL语句加锁和事物隔离级别,访问的索引是不是唯一,访问数据是否存在都有关系,往往死锁分析非常复杂。这篇文章将介绍一个“简单的死锁”,这个死锁产生的事物中SQL语句都只有一条,而且业务非常简单就是删除一条记录。两个事物同时执行以下两个SQL语句就有可能死锁。 DELETE FROM dept_manager WHERE num = 0; DELETE FROM dept_manager WHERE dept_no = 'd001'; 一、死锁模拟 死锁模拟 首先介绍下表结构,这个表除了主键索引 PRIMARY,还有一个唯一索引 num 和一个非唯一索引 dept_no ,建表语句如下: CREATE TABLE `dept_manager` ( `emp_no` int(11) NOT NULL, `dept_no` char(4) NOT NULL, `num` int(11) NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`), unique index(`num`), KEY `dept_no` (`dept_no`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; 然后再准备下数据: INSERT INTO `dept_manager` VALUES (1001,'d001',0,'1991-10-01'), (1002,'d005',1,'9999-01-01'), (1005,'d002',3,'1989-12-17'), (1007,'d002无印良品公关危机',4,'9999-01-01'), (1008,'d004',7,'1988-09-09'), (1009,'d004',8,'1992-08-02'), (1010,'d005',9,'1996-08-30'); 使用执行两个sql很难,使用 mysqlslap 来高并发碰碰运气: # mysqlslap --create-schema dldb -q "begin;DELETE FROM dept_manager WHERE num = 0; rollback;" --number-of-queries=100000 -uroot -p123456 & # mysqlslap --create-schema dldb -q "begin;DELETE FROM dept_manager WHERE dept_no = 'd001'; rollback;" --number-of-queries=100000 -uroot -p123456 & 这两个事物非常都是删除一行相同的数据 (1001,'d001',0,'1991-10-01')只不过一个根据索引 num ,一个根据索引 dept_no 。 二、原公关危机失败案例的PPT因分析 1. 数据是怎么找到的? 要说清楚死锁产生原因,就要先理清楚这条SQL是怎么执行的,会在那些地方加锁。在此之前先说说数据库是怎么找到我们要删除的这行数据的。下面两幅图展示根据年龄为30来查记录的示意图。首先根据 name 为 seven, 在 name 这个辅助索引查找,但是只能拿到主键的 id。随后再根据主键id 去主键查找,这个过程称为回表。访问数据是要通过索引的,而且数据就在主键索引上面,所以加锁就是加在索引上面的。 2. Delete 是怎么执行的 Delete 删除数据其实并不是把数据删除了,只是把数据标记一下,表示这里可以复用的,如果下次这里有数据要插入就可以直接复用原来空间里。所以Delete 和 Update 操作比较类似。Delete 和 Update 是根据条件找到第一条数据,进行修改,然后找到第二条数据,以此类推直到再也查不到符合条件的数据。 3. 加锁分析 我们以 DELETE FROM dept_manager WHERE num = 0; 为例,只有一个条件 num = 0, 因该是根据 num = 0 在 num 索引中找到对应的主键id, 随后根据主键 id,找到对应记录,标记成可复用状态。除了删除数据行记录,对应的索引也需要维护下,其他索引对应位置也需要标记成删除状态。这个表中主键索引 PRIMARY,唯一索引 num,非唯一索引 dept_no 的对应位置都会加上锁。同理第二个SQL语句执行时候,加锁位置也是一样的。(可重复度隔离级别上,非唯一索引还要加上间隙锁)。 既然加锁上一样的,那应该是在不同索引加锁顺序是不一样的。推测下对于 WHERE num = 0 应该先在 num 上加锁,随后在主键加锁,最后在 dept_no上,num ->PRIMARY-> dept_no。WHERE dept_no = 'd001';加锁顺序应该是dept_no -> PRIMARY -> num。尽管这条SQL数据很简单,但是由于数据中索引比较多,加锁顺序也不一样,导致了死锁。 三、场景验证 可以用 show engine innodb status ,来查看最近一次死锁日志。事物1等待索引dept上的锁 0: len 4; hex 64303031; asc d001;; 这里“64303031” 16进制转为字符为“d001” 与 WHERE dept_no = 'd001' 相对应。事物2持有这个锁的,事物1持有的锁没有显示,应该是主键上的锁,这是符合预期的。 ------------------------ LATEST DETECTED DEADLOCK ------------------------ 2021-04-27 16:41:19 0x70000a6b1000 *** (1) TRANSACTION: TRANSACTION 1681994, ACTIVE 0 sec updating or deleting mysql tables in use 1, locked 1 LOCK WAIT 4 lock struct(s), heap size 1136, 3 row lock(s), undo log entries 1 MySQL thread id 30, OS thread handle 123145456488448, query id 343687 localhost 127.0.0.1 root updating DELETE FROM dept_manager WHERE num = 0 *** (1) WAITING FOR THIS LOCK TO BE GRANTED: #请求 dept_no上锁 RECORD LOCKS space id 367 page no 5 n bits 80 index dept_no of table `employees`.`dept_manager` trx id 1681994 lock_mode X locks rec but not gap waiting Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 64303031; asc d001;; 1: len 4; hex 800003e9; asc ;; *** (2) TRANSACTION: TRANSACTION 1681554, ACTIVE 0 sec starting index read mysql tables in use 1, locked 1 3 lock struct(s), heap size 1136, 2 row lock(s) MySQL thread id 106, OS thread handle 123145477099520, query id 341105 localhost 127.0.0.1 root updating DELETE FROM dept_manager WHERE dept_no = 'd001' *** (2) HOLDS THE LOCK(S): # 持有 dept_no 上锁 RECORD LOCKS space id 367 page no 5 n bits 80 index dept_no of table `employees`.`dept_manager` trx id 1681554 lock_mode X Record lock, heap no 2 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 0: len 4; hex 64303031; asc d001;; 1: len 4; hex 800003e9; asc ;; *** (2) WAITING FOR THIS LOCK TO BE GRANTED: # 请求主键 RECORD LOCKS space id 367 page no 3 n bits 80 index PRIMARY of table `employees`.`dept_manager` trx id 1681554 lock_mode X locks rec but not gap waiting Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 32 0: len 4; hex 800003e9; asc ;; 1: len 6; hex 00000019aa4a; asc J;; 2: len 7; hex 2c000001b80ede; asc , ;; 3: len 4; hex 64303031; asc d001;; 4: len 4; hex 80000000; asc ;; 5: len 3; hex 8f8f41; asc A;; *** WE ROLL BACK TRANSACTION (2) 四、总结 本文介绍的样例中,尽管SQL语句很简单,但由于表中有多个索引,对索引的访问顺序不同,造成死锁风险。为了避免数据库中发生死锁,建议: 1. 尽量开启死锁检测; 2. 尽量使用小事务,在业务允许范围内,将隔离级别改成读已提交,可以减少不些不必要的锁; 3. 避免全表扫描; 4. 避免较多索引; 5. 不同事务对表和行操作的顺序尽量一致。 |
上一篇:Lazada快消品情报局丨吃鸡行情打哪来?快消Top4类
下一篇:腾讯云:使用ASW工作流创建您的第一个函数编排
小提示:您应该对本页介绍的“腾讯云:这个不可思议的死锁你会解吗?”相关内容感兴趣,若您有相关需求欢迎拨打我们的服务热线或留言咨询,我们尽快与您联系沟通腾讯云:这个不可思议的死锁你会解吗?的相关事宜。
关键词:腾讯云:这个不可思议的