MySQL DELETE 删除语句加锁分析

Posted on 2017-09-24 by Harvey Leave a comment  MySQL

http://www.fordba.com/lock-analyse-of-delete.html

1. 前言

在MySQL的使用过程中,对SQL加锁的类型经常感到疑惑,这让死锁分析也变得举步维艰。因此需要将MySQL的各种SQL在各个隔离级别下加的锁进行分析,以免再次分析的时候还感到疑惑,也方便用于查询。

本次分析对SQL的删除语句进行分析,主要从以下几种情况进行分析:

  1. 非唯一索引删除一条存在的记录
  2. 唯一索引删除一条存在的记录
  3. 主键删除一条存在的记录
  4. 非唯一索引删除一条不存在记录
  5. 唯一索引删除一条不存在的记录
  6. 主键删除一条不存在的记录
  7. 不同的SQL根据主键删除2条记录
  8. 非唯一索引删除一条已经标记删除的记录
  9. 唯一索引删除一条已经标记删除的记录

在使用之前需要打开innodb lock monitor,这样在查看 engine innodb status 的时候可以更加清晰的查到到锁的情况

set GLOBAL innodb_status_output_locks=ON;

2. SQL的加锁分析

相关表结构

  • 普通索引表结构
CREATE TABLE `t` (`id` int(11) NOT NULL AUTO_INCREMENT,`c1` int(11) NOT NULL DEFAULT '0',`c2` int(11) NOT NULL DEFAULT '0',PRIMARY KEY (`id`),KEY `idx_c1` (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4;
  • 唯一索引表结构
CREATE TABLE `tu` (`id` int(11) NOT NULL AUTO_INCREMENT,`c1` int(11) NOT NULL DEFAULT '0',`c2` int(11) NOT NULL DEFAULT '0',PRIMARY KEY (`id`),UNIQUE KEY `uniq_c1` (`c1`)
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb4
  • 表的记录,唯一索引和普通索引的表结构均一样
  • 测试的事务隔离级别为RR。
+----+----+----+
| id | c1 | c2 |
+----+----+----+
|  2 |  3 |  2 |
|  3 |  5 |  3 |
|  4 |  8 |  4 |
|  5 | 11 |  5 |
|  9 |  9 | 20 |
| 10 |  7 | 10 |
| 11 | 20 | 15 |
| 12 | 30 | 17 |
| 13 | 25 | 16 |
| 14 | 27 | 10 |
+----+----+----+

2.1 删除SQL加锁分析

根据非唯一索引删除一条存在记录

delete from t where c1=5;
Query OK, 1 rows affected (0.00 sec)---TRANSACTION 146749, ACTIVE 9 sec
4 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 1
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 104 localhost msandbox cleaning up
TABLE LOCK table `test`.`t` trx id 146749 lock mode IX
RECORD LOCKS space id 53 page no 5 n bits 72 index `idx_c1` of table `test`.`t` trx id 146749 lock_mode X
RECORD LOCKS space id 53 page no 3 n bits 72 index `PRIMARY` of table `test`.`t` trx id 146749 lock_mode X locks rec but not gap
RECORD LOCKS space id 53 page no 5 n bits 72 index `idx_c1` of table `test`.`t` trx id 146749 lock_mode X locks gap before rec

根据非唯一索引进行删除的时候,锁情况为:

  1. 4 lock struct(s):4种锁结构,分别为IX,idx_c1的next key 锁(3,5] ,主键的行锁,还有idx_c1的gap锁(5,7)
  2. 3 row lock(s):除去IX的都是算在row lock里面

根据唯一索引删除一条存在记录

delete from tu where c1=5;
Query OK, 1 rows affected (0.00 sec)---TRANSACTION 146751, ACTIVE 2 sec
3 lock struct(s), heap size 360, 2 row lock(s), undo log entries 1
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 134 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146751 lock mode IX
RECORD LOCKS space id 45 page no 5 n bits 72 index `uniq_c1` of table `test`.`tu` trx id 146751 lock_mode X locks rec but not gap
RECORD LOCKS space id 45 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146751 lock_mode X locks rec but not gap

根据唯一索引进行删除的时候,锁情况为:

  1. 3 lock struct(s):3种锁结构,分别为IX,idx_c1和主键的行锁,没有gap锁
  2. 2 row lock(s):除去IX的都是算在row lock里面,没有gap,因此为2个

根据主键删除一条存在记录

delete from tu where id=2;
Query OK, 1 rows affected (0.00 sec)---TRANSACTION 146753, ACTIVE 2 sec
2 lock struct(s), heap size 360, 1 row lock(s), undo log entries 1
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 147 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146753 lock mode IX
RECORD LOCKS space id 45 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146753 lock_mode X locks rec but not gap

根据主键进行删除的时候,锁情况为:

  1. 2 lock struct(s):2种锁结构,分别为IX和主键的行锁,没有gap锁
  2. 1 row lock(s):就主键记录上的行锁,没有gap,因此为1个

根据非唯一索引删除一条不存在 记录

delete from t where c1 = 4;
Query OK, 0 rows affected (0.00 sec)---TRANSACTION 146786, ACTIVE 1 sec
2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 671 localhost msandbox cleaning up
TABLE LOCK table `test`.`t` trx id 146786 lock mode IX
RECORD LOCKS space id 53 page no 5 n bits 80 index `idx_c1` of table `test`.`t` trx id 146786 lock_mode X locks gap before rec

根据非唯一索引删除一条 不存在 记录,锁情况为:

  1. 2 lock struct(s):2种锁结构,分别为IX和X类型的gap锁
  2. 1 row lock(s):为非唯一索引的gap(X)锁

根据唯一索引删除一条不存在 记录

delete from tu where c1 = 4;
Query OK, 0 rows affected (0.00 sec)---TRANSACTION 146787, ACTIVE 2 sec
2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 711 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146787 lock mode IX
RECORD LOCKS space id 45 page no 5 n bits 72 index `uniq_c1` of table `test`.`tu` trx id 146787 lock_mode X locks gap before rec

根据唯一索引删除一条 不存在 记录,发现和非唯一索引一样,锁情况为:

  1. 2 lock struct(s):2种锁结构,分别为IX和X类型的gap锁
  2. 1 row lock(s):为唯一索引的gap(X)锁

根据主键删除一条不存在 记录

delete from tu where id = 6;
Query OK, 0 rows affected (0.00 sec)---TRANSACTION 146831, ACTIVE 24 sec
2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 881 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146831 lock mode IX
RECORD LOCKS space id 45 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146831 lock_mode X locks gap before rec

根据主键删除一条 不存在 记录,发现和非唯一索引一样,锁情况为:

  1. 2 lock struct(s):2种锁结构,分别为IX和X类型的gap锁
  2. 1 row lock(s):为主键上的gap(X)锁

根据主键删除两条存在的记录

有 5 , 10 这两条记录delete from tu where id>=5 and id<10;
Query OK, 2 rows affected (0.00 sec)---TRANSACTION 146900, ACTIVE 35 sec
3 lock struct(s), heap size 360, 3 row lock(s), undo log entries 2
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 995 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146900 lock mode IX
RECORD LOCKS space id 56 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146900 lock_mode X locks rec but not gap
RECORD LOCKS space id 56 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146900 lock_mode X
有 5 , 9 这两条记录delete from tu where id>=5 and id<=9;
Query OK, 2 rows affected (0.00 sec)---TRANSACTION 146912, ACTIVE 12 sec
3 lock struct(s), heap size 360, 3 row lock(s), undo log entries 2
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 1022 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146912 lock mode IX
RECORD LOCKS space id 56 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146912 lock_mode X locks rec but not gap
RECORD LOCKS space id 56 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146912 lock_mode X
有 4 ,10 这两条记录delete from tu where id>4 and id<10;
Query OK, 2 rows affected (0.00 sec)---TRANSACTION 146906, ACTIVE 13 sec
2 lock struct(s), heap size 360, 3 row lock(s), undo log entries 2
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 1011 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146906 lock mode IX
RECORD LOCKS space id 56 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146906 lock_mode X
有 10 没 7 delete from tu where id>=7 and id<=10;
Query OK, 2 rows affected (0.00 sec)---TRANSACTION 146966, ACTIVE 2 sec
2 lock struct(s), heap size 360, 3 row lock(s), undo log entries 2
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 1172 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146966 lock mode IX
RECORD LOCKS space id 57 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146966 lock_mode X
有 4没 8 delete from tu where id>=4 and id<=8;
Query OK, 2 rows affected (0.00 sec)---TRANSACTION 146972, ACTIVE 20 sec
3 lock struct(s), heap size 360, 3 row lock(s), undo log entries 2
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 1201 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146972 lock mode IX
RECORD LOCKS space id 57 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146972 lock_mode X locks rec but not gap
RECORD LOCKS space id 57 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146972 lock_mode X
有3,4两条记录delete from tu where id in (3,4);
Query OK, 2 rows affected (0.00 sec)---TRANSACTION 146880, ACTIVE 1 sec
2 lock struct(s), heap size 360, 2 row lock(s), undo log entries 2
MySQL thread id 1, OS thread handle 0x7f61ab1c7700, query id 928 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146880 lock mode IX
RECORD LOCKS space id 56 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146880 lock_mode X locks rec but not gap

根据主键删除两条的时候,使用in的锁情况为:

  1. 2 lock struct(s):2种锁结构,分别为IX和i主键的行锁,没有gap锁
  2. 2 row lock(s):两条主键记录上的行锁,没有gap,因此为2个

根据主键删除两条的时候,使用>,<,>=,<=,比较符号的锁情况为:

  1. 无论如何,匹配到2条记录,因此必须会有2 row lock(s)
  2. 如果只有>,<,那么毫无疑问,是不会锁定两个边界的记录,因此他只会锁定边界到边界内的整个范围,锁的类型为next key,此时为2 lock struct(s) ,3 row lock(s)
  3. 碰到 >= 的时候,判断 >= 的值是否存在,如果存在,则锁定该记录。所以除了IX,next key锁,还有行锁,因此存在的时候为3 lock struct(s), 3 row lock(s)。如果不存在,和第二种是一样的,为2 lock struct(s) ,3 row lock(s) 。

非唯一索引删除一条已经标记删除的记录

Sess1 Sess2 Sess3
begin;
delete from t where c1=8;
begin;
delete from t where c1=8;
@1 show engine innodb status
commit;
@2 show engine innodb status
@1 show engine innodb status---TRANSACTION 146981, ACTIVE 12 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 363, OS thread handle 0x7f61ab1c7700, query id 2804 localhost msandbox updating
delete from t where c1=8
------- TRX HAS BEEN WAITING 12 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 54 page no 4 n bits 80 index `idx_c1` of table `test`.`t` trx id 146981 lock_mode X waiting
------------------
TABLE LOCK table `test`.`t` trx id 146981 lock mode IX
RECORD LOCKS space id 54 page no 4 n bits 80 index `idx_c1` of table `test`.`t` trx id 146981 lock_mode X waiting
---TRANSACTION 146980, ACTIVE 16 sec
4 lock struct(s), heap size 1184, 3 row lock(s), undo log entries 1
MySQL thread id 355, OS thread handle 0x7f61ab145700, query id 2802 localhost msandbox cleaning up
TABLE LOCK table `test`.`t` trx id 146980 lock mode IX
RECORD LOCKS space id 54 page no 4 n bits 80 index `idx_c1` of table `test`.`t` trx id 146980 lock_mode X
RECORD LOCKS space id 54 page no 3 n bits 80 index `PRIMARY` of table `test`.`t` trx id 146980 lock_mode X locks rec but not gap
RECORD LOCKS space id 54 page no 4 n bits 80 index `idx_c1` of table `test`.`t` trx id 146980 lock_mode X locks gap before rec@2 show engine innodb status---TRANSACTION 146981, ACTIVE 50 sec
3 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 363, OS thread handle 0x7f61ab1c7700, query id 2804 localhost msandbox cleaning up
TABLE LOCK table `test`.`t` trx id 146981 lock mode IX
RECORD LOCKS space id 54 page no 4 n bits 80 index `idx_c1` of table `test`.`t` trx id 146981 lock_mode X
RECORD LOCKS space id 54 page no 4 n bits 80 index `idx_c1` of table `test`.`t` trx id 146981 lock_mode X locks gap before rec

非唯一索引删除一条已经标记删除的记录的锁情况为:

  • 加锁等待时: 2 lock struct(s) ,持有IX锁,等待记录上的next key 锁
  • 加锁成功时:3 lock struct(s),持有IX,next key 锁,和gap锁,这个和非唯一索引删除一条不存在的记录是基本一样的,多了个因Sess1 提交成功后多获得的next key 锁。

唯一索引删除一条已经标记删除的记录

Sess1 Sess2 Sess3
begin;
delete from tu where c1=8;
begin;
delete from tu where c1=8;
@1 show engine innodb status
commit;
@2 show engine innodb status
@1 show engine innodb status---TRANSACTION 146984, ACTIVE 2 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 363, OS thread handle 0x7f61ab1c7700, query id 2842 localhost msandbox updating
delete from tu where c1=8
------- TRX HAS BEEN WAITING 2 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 57 page no 4 n bits 80 index `uniq_c1` of table `test`.`tu` trx id 146984 lock_mode X waiting
------------------
TABLE LOCK table `test`.`tu` trx id 146984 lock mode IX
RECORD LOCKS space id 57 page no 4 n bits 80 index `uniq_c1` of table `test`.`tu` trx id 146984 lock_mode X waiting
---TRANSACTION 146983, ACTIVE 9 sec
3 lock struct(s), heap size 360, 2 row lock(s), undo log entries 1
MySQL thread id 355, OS thread handle 0x7f61ab145700, query id 2839 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146983 lock mode IX
RECORD LOCKS space id 57 page no 4 n bits 80 index `uniq_c1` of table `test`.`tu` trx id 146983 lock_mode X locks rec but not gap
RECORD LOCKS space id 57 page no 3 n bits 80 index `PRIMARY` of table `test`.`tu` trx id 146983 lock_mode X locks rec but not gap@2 show engine innodb status---TRANSACTION 146984, ACTIVE 23 sec
3 lock struct(s), heap size 360, 1 row lock(s)
MySQL thread id 363, OS thread handle 0x7f61ab1c7700, query id 2842 localhost msandbox cleaning up
TABLE LOCK table `test`.`tu` trx id 146984 lock mode IX
RECORD LOCKS space id 57 page no 4 n bits 80 index `uniq_c1` of table `test`.`tu` trx id 146984 lock_mode X
RECORD LOCKS space id 57 page no 4 n bits 80 index `uniq_c1` of table `test`.`tu` trx id 146984 lock_mode X locks gap before rec

唯一索引删除一条已经标记删除的记录的锁情况为:

  • 加锁等待时: 2 lock struct(s) ,持有IX锁,等待记录上的next key 锁

  • 加锁成功时:3 lock struct(s),持有IX,next key ,和gap锁,和非唯一索引删除一条标记为已删除的记录的情况一模一样。

3. 总结

  1. 在非唯一索引的情况下,删除一条存在的记录是有gap锁,锁住记录本身和记录之前的gap
  2. 在唯一索引和主键的情况下删除一条存在的记录,因为都是唯一值,进行删除的时候,是不会有gap存在
  3. 非唯一索引,唯一索引和主键在删除一条不存在的记录,均会在这个区间加gap锁
  4. 通过非唯一索引和唯一索引去删除一条标记为删除的记录的时候,都会请求该记录的行锁,同时锁住记录之前的gap
  5. RC 情况下是没有gap锁的,除了遇到唯一键冲突的情况,如插入唯一键冲突。

MySQL DELETE 删除语句加锁分析相关推荐

  1. Mysql中删除语句delete、truncate、drop的区别

    Mysql中删除语句delete.truncate.drop的区别 1.DROP: 用法: DROP TABLE 表名 DDL语句,删除数据和表结构,释放空间资源.注意:操作不放到rollback s ...

  2. c++客户端发送加锁_MySQL语句加锁分析详解

    前言 建立一个存储三国英雄的hero表: CREATE TABLE hero ( number INT, name VARCHAR(100), country varchar(100), PRIMAR ...

  3. MySQL数据删除语句

    MySQL数据删除语句 在 MySQL中,可以使用 DELETE 语句来删除表的一行或者多行数据. 基础语法 删除单个表中的数据 使用 DELETE 语句从单个表中删除数据,语法格式为: DELETE ...

  4. 数据库:delete 删除语句

    --delete删除语句 --语法: 1:删除整表:delete from 表名 2:删除表中的某一行:delete from 表名 where 过滤语句. --实例: delete from bon ...

  5. 零基础自学SQL课程 | DELETE 删除语句

    大家好,我是宁一. 今天是SQL课程的第十课. 讲讲DELETE 删除语句,用来删除表中的一条或多条记录. 基本语法: DELETE FROM <表名> WHERE <筛选条件> ...

  6. SQL入门之第九讲——DELETE删除语句

    定义:DELETE 语句是用于删除表中的一条或多条记录. 基本语法: DELETE FROM <表名> WHERE <筛选条件>; 1.DELETE删除语句 如果省略WHERE ...

  7. MySQL的并发控制与加锁分析

    2019独角兽企业重金招聘Python工程师标准>>> 本文主要是针对MySQL/InnoDB的并发控制和加锁技术做一个比较深入的剖析,并且对其中涉及到的重要的概念,如多版本并发控制 ...

  8. mysql bytessent_如何对DSQLSERVER、MySQL、Orache语句性能分析

    SQLSERVER语句性能分析 SQL SERVER通过设置STATISTICS查看执行SQL时的系统情况. SET STATISTICS PROFILE ON:显示分析.编译和执行查询所需的时间(以 ...

  9. mysql delete|删除 命令的注意点

    2019独角兽企业重金招聘Python工程师标准>>> 今天,写一条mysql删除语句,我记忆中的语句一直是这样子的: delete FROM user u where u.user ...

最新文章

  1. 站在巨人的肩膀上“思考”问题,重在思考而不是拿来主义
  2. 流数据机器学习平台SAMOA简介
  3. Go加密解密之DES
  4. 关于产品的一点点想法
  5. DPCM 压缩系统的实现和分析
  6. JAVA取模(取余)公式
  7. TCP协议连环12问
  8. mysql启动提示 access denied for user root@localhost(using password:YES) 解决办法总结
  9. 仿新浪新闻中异步替换关键字
  10. Pycharm远程调试报错:undefined symbol: AttachDebuggerTracing
  11. odoo tree视图过滤数据_OpenERP Odoo 搜索视图设置默认过滤按钮(filter button)
  12. python函数支持哪些参数类型_Python函数的几种参数类型
  13. 离散数学学习笔记----命题逻辑的推理理论
  14. 高速公路收费标准c语言,c语言课程设计报告-高速公路收费系统
  15. UVA 213 - Message Decoding 简单题 lambda表达式 23333333
  16. 从github上下载文件,文件夹,整个项目
  17. 关于一政网教育,考生们是如何看待的?
  18. win2003 启用了Internet Explorer增强的安全配置
  19. 跨模态对齐 20220728
  20. 成功的捷径就是没有捷径

热门文章

  1. 用GPE发掘各版块优质成长标的
  2. 解决Apple TV 4K 关机同时关闭小米电视
  3. 大数据产业核心产业环节_产业实力自然语言处理
  4. 单片机c语言字符,单片机C语言(一) - 控制/MCU - 电子发烧友网
  5. 趋势、马丁、对冲结合ea的编程实践(八)编程思路分享及效果展示
  6. OSChina 周六乱弹 ——养了一只孙红雷
  7. 10行python代码可以做哪些酷炫的事情
  8. linux vim 格式化,vim格式化
  9. 网页制作动态技术----PHP
  10. 我在清华等你——桐华《最美的时光》读后感