背景

MySQL中SQL加锁的情况十分复杂,不同隔离级别、不同索引类型、索引是否命中的SQL加锁各不相同。
然而在分析死锁过程当中,熟知各种情况的SQL加锁是分析死锁的关键,因此需要将MySQL的各种SQL情况加锁进行分析总结。 
 
基础知识

MVCC
  • 快照读

    • 读取历史版本,从undo log中读取行记录的快照;这样读行就不需要等待锁资源,提高了并发;
  • 当前读
    • 读取最新版本,并且当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。
    • 加锁读、插入、更新、删除等操作均属于当前读
 
将插入,更新,删除归为当前读是因为这些操作均包含读取当前记录的操作。拿update table set ? where ?来讲,
当Update SQL被发给MySQL后,MySQL Server会根据where条件,读取第一条满足条件的记录,然后InnoDB引擎会将第一条记录返回,并加锁 (current read)。
待MySQL Server收到这条加锁的记录之后,会再发起一个Update请求,更新这条记录。一条记录操作完成,再读取下一条记录,直至没有满足条件的记录为止。
因此,Update操作内部,就包含了一个当前读。同理,Delete操作也一样。Insert操作会稍微有些不同,简单来说,就是Insert操作可能会触发Unique Key的冲突检查,也会进行一个当前读。
 
注意:
Innodb当前读加锁是一条一条进行,先对一条满足条件的记录加锁,返回给MySQL Server做一些DML操作,然后在读取下一条加锁,直至读取完毕。 
 

Two-phase locking
Two-Phase Locking,说的是锁操作分为两个阶段:加锁阶段与解锁阶段,并且保证加锁阶段与解锁阶段不相交。 加锁阶段:只加锁,不放锁。解锁阶段:只放锁,不加锁。 
 

隔离级别
 
数据库的隔离现象与隔离级别如下图所示
 
脏读现象:即A连接,未提交的事务,B连接的事务可以看到;
 
NONREPEATABLE READ(不可重复读)现象:
A连接,提交的事务,B连接的事务中可以看到,这样在B连接中的事务,就可以看到A连接事务提交前,和提交后两种状态;
注意:不可重复读针对update,delete
 

PHANTOM READ(幻读)现象:
A连接,提交的事务,B连接的事务可以看到,这样在B连接中的事务,就可以看到A连接事务提交前,和提交后两种状态;
注意:幻读针对insert;
 
innodb事务引擎,通过间隙锁 粗暴 将RR隔离级别的幻读现象消除,这也是RR与RC的主要区别。
 
基础SQL组合分析

使用下面这张 students 表作为实例,其中 id 为主键,no(学号)为二级唯一索引, score(学分)为二级非唯一索引,age(年龄)无索引。
 
我们只分析基础SQL,它只包含一个 WHERE 条件,等值查询或范围查询,根据条件类型以及隔离级别不同(主要分析最常用的RR,RC)我们主要分析一下情况: 

 
聚簇索引,索引命中
SQL select * from students where id = 20  for update,  在 RC 和 RR 隔离级别下加锁情况一样,都是对 id 这个聚簇索引加 X 锁,如下:
 
唯一索引,索引命中
SQL:select * from students where num = 135 for update;
若检索唯一索引,那么SQL需要加两个X锁,一个对应唯一索引上的num = 135的记录,另一把锁对应于聚簇索引上的[id=35]的记录。
 
二级索引,索引命中
SQL:select * from students where score= 91  for update; 
如此例子当中如法插入score 值有[77,99),注意边界值。前边界77是无法插入的,后边界99则可以插入。
此外 select * from students where score= 77  for update,是不用等待锁的。
 
无索引
 

SQL: select * from students where age = 22  for update;
无索引时如何是RR还是RC均会将行锁升级为表锁,具体表现就是全表update,delete。
此外因为RR隔离级别有next-key,RR除了不能update,delete外连insert都不可以,而RC则可以进行insert 操作。
 
索引未命中

 

聚簇索引,索引未命中
SQL  select * from students where id = 30  for update;
RR隔离级别下,当查找聚簇索引但索引未命中时,此时聚簇索引加锁状态与二级索引状态相同,原本行锁变为gap锁,锁范围如下:
  • (25,35)
当然此时收主键唯一性约束,任何插入id=25或35的操作均会失败,这一点与二级索引不同。
RC隔离级别下,由于id索引未命中即聚簇索引中没有相关记录,则不加任何锁。
 

唯一索引,索引未命中
SQL  select * from students where num = 130  for update;
唯一索引,索引未命中的情况与上面聚簇索引,索引未命中的情况 相似。区别在于聚簇索引gap锁加载聚簇表中,唯一索引则在唯一索引自身的索引表中。
同样是没有行锁,仅有gap锁,其表现出来的现象就是在gap范围内如法插入数据,不影响其余DML操作。
 
二级索引,索引未命中
SQL  select * from students where score = 70  for update;
范围查询

  • 聚簇索引范围查询

    • select * from students where id <=25 for update;
RR隔离级别时,聚簇索引范围查询时加锁情况如下图。
如果where 条件为id<25 则在25-35间不会加GAP锁,但也会在25上加X锁,然后再在相应范围加GAP锁。
如果where 条件为id>25,并不会在25处加X锁,仅会在(25,+)加GAP锁以及对应索引项加X锁。
 
注意:在RR隔离级别时,条件y<id<x, 则MySQL选择比y,x大的索引项来加X锁,称为向右扩展
 
 
  • 唯一索引范围查询

    • select * from students where num >125 for update;
唯一索引范围查询整体与聚簇索引范围查询相似,RC仅在范围内的索引列上加X锁。RR则除在索引列上在X锁外,还会在范围内索引列之间加GAP锁。
此外RR的边界值是否加X锁,有向右扩展原则即向索引值大的方向扩展加X锁。当num<125时,向右扩展的第一个索引值为125 则会在125上加X锁。
当num<=125时,向右扩展的第一个索引值为135,则会在135上加X锁。当num>125时,向右扩展的第一个索引值为135,包含在范围之内因此无特殊表现。 
 

  • 二级索引范围查询
select * from students where score <= 50 for update;
由下图可见二级索引范围查询其实与唯一索引以及聚簇索引的范围查询的加锁原理相同。RC仅在范围内的索引项上加X锁。
RR则除范围内索引项加X锁外,并在索引项间加GAP锁,且边界值是否加X锁遵循向右扩展原则
小结
  • 索引等值查询,且索引命中

    • 主键、唯一索引无论RR或RC均在索引项及其聚簇索引对应记录上加X锁。
    • 二级索引RC隔离级别与主键、唯一索引相同
    • 二级索引RR隔离级别,除对应索引项及其记录上加X锁外,在各索引项间加GAP锁
  • 索引等值查询,且索引未命中
    • RR主键,与唯一索引会在包含条件值得两个索引项间 加GAP锁
    • 二级索引与主键、唯一索引相似,也会在包含条件值的两个索引间加GAP锁并在左侧索引项上加X锁
    • RC不加任何锁
  • 索引范围查询
    • 主键索引,唯一索引,二级索引加锁原理相同。

      • RC仅在范围内的索引项上加X锁。
      • RR则除范围内索引项加X锁外,并在索引项间加GAP锁,且边界值是否加X锁遵循向右扩展原则
 

另一个角度总结
  • RC

    • RC隔离级别没有GAP锁(唯一索引insert情况除外,仅指select情况),仅在符合条件的索引项上加X锁
  • RR
    • RR隔离基本多了GAP锁,但在主键或唯一索引存在时仅在索引项及其记录上加X锁,不加GAP锁。

      • 二级锁索引则除索引项及其记录上加X锁外,并在包含X锁记录的两侧索引项之间加GAP锁。
    • 若索引值未命中
      • 主键,唯一索引,二级索引均会在包含未命中索引值得两侧索引项之间加GAP锁。
      • 若是二级索引还会在左侧索引项上加X锁。
 

where 条件提取

 
给定一条SQL,索引项是如何影响查询过程的,非索引项的条件是如何过滤数据,只有清楚掌握每个细节才能写出性能较高的SQL语句。
SQL的where条件大约分为3类
  • index key
  • index filter
  • table filter
 
Index Key
确定索引扫描的范围,其包括起始位置与终止位置, 因此Index Key也被拆分为Index First Key和Index Last Key,
分别用于定位索引查找的起始,以及索引查询的终止条件。
 
Frist Key
用于确定索引查询的起始范围。
提取规则:从索引的第一个键值开始,检查其在where条件中是否存在,若存在并且条件是=、>=,则将对应的条件加入Index First Key之中,继续读取索引的下一个键值,使用同样的提取规则;若存在并且条件是>,则将对应的条件加入Index First Key中,同时终止Index First Key 提取;若不存在,同样终止Index First Key 提取
 
例如
idx_c1_c2_c3(c1,c2,c3)
where c1>=1 and c2>2 and c3=1
-->  first key (c1,c2)
--> c1为 '>=' ,加入下边界界定,继续匹配下一个
--> c2 为 '>', 加入下边界界定,停止匹配
 
Last Key
用于确定索引查询的终止范围.
提取规则:从索引的第一个键值开始,检查其在where条件中是否存在,若存在并且条件是=、<=,则将对应条件加入到Index Last Key中,继续提取索引的下一个键值,使用同样的提取规则;若存在并且条件是 < ,则将条件加入到Index Last Key中,同时终止提取;若不存在,同样终止Index Last Key的提取。
 
例如
idx_c1_c2_c3(c1,c2,c3)
where c1<=1 and c2=2 and c3<3
--> last key (c1,c2,c3)
--> c1为 '<=',加入上边界界定,继续匹配下一个
--> c2为 '='加入上边界界定,继续匹配下一个
--> c3 为 '<',加入上边界界定,停止匹配
 
注意:提取过程中 如果比较符号中包含'='号,'>='也是包含'=',那么该索引键是可以被利用的,可以继续匹配后面的索引键值;如果不存在'=',也就是'>','<',这两个,后面的索引键值就无法匹配了。
 
Index Filter
字面理解就是可以用索引去过滤。也就是字段在索引键值中,但是无法用去确定Index Key的部分。
 
exp:
idex_c1_c2_c3
where c1>=1 and c2<=2 and c3 =1
index key --> c1
index filter--> c2 c3
 
这里为什么index key 只是c1呢?因为c2 是用来确定上边界的,但是上边界的c1没有出现(<=,=),而下边界中,c1是>=,c2没有出现,因此index key 只有c1字段。c2,c3 都出现在索引中,被当做index filter.
 
MySQL 是 5.6 之前的版本,Index Filter 和 Table Filter 没有区别,统统将 Index First Key 与 Index Last Key 范围内的索引记录,回表读取完整记录,然后返回给 MySQL Server 层进行过滤。而在 MySQL 5.6 之后,Index Filter 与 Table Filter 分离,Index Filter 下降到 InnoDB 的索引层面进行过滤,减少了回表与返回 MySQL Server 层的记录交互开销,提高了SQL的执行效率,这就是 ICP(Index Condition Pushdown)
 
Table Filter
无法利用索引完成过滤,就只能用table filter。此时引擎层会将行数据返回到server层,然后server层进行table filter。
 
举例来说 
 
Index Key  : pubtime
Index Filter:   userid
Table Filter:   comment
 
若使用5.6之前的版本则红色箭头线所指的记录会加X锁,因为5.6之前Index Filter与Table Filter作用一样,都需要根据Index Key 的扫描范围回表,到server层再过滤。
若使用5.6及其之后的版本则红色箭头线缩指的记录不会加X锁,因为ICP(Index Condition Pushdown)特性,在Index Key 扫描完范围后,根据Index Filter过滤掉不符合要求的然后在回表到server层去过滤Table Filter 即找到comment not  null的记录在该条记录上加X锁。 

SQL语句加锁分析的更多相关文章

  1. MySQL innodb中各种SQL语句加锁分析

    概要 Locking read( SELECT ... FOR UPDATE or SELECT ... LOCK IN SHARE MODE),UPDATE以及DELETE语句通常会在他扫描的索引所 ...

  2. MySQL中一条SQL的加锁分析

    MySQL中一条SQL的加锁分析 id主键 + RC id唯一索引 + RC id非唯一索引 + RC id无索引 + RC id主键 + RR id唯一索引 + RR id非唯一索引 + RR id ...

  3. SQL语句性能分析

    SQL语句性能分析 explain执行计划 用法: explain select 语句 命令: show database; use mysql explain select * from user; ...

  4. 看懂SqlServer查询计划 SQL语句优化分析

    转自 http://www.cnblogs.com/fish-li/archive/2011/06/06/2073626.html 阅读目录 开始 SQL Server 查找记录的方法 SQL Ser ...

  5. SQL语句优化分析

    分析比较执行时间计划读取情况 select * from dbo.Product 执行上面语句一般情况下只给你返回结果和执行行数,那么你怎么分析呢,怎么知道优化之后跟没有优化的区别呢. 下面几种方法: ...

  6. SQL 语句 explain 分析

      分析索引的效率: > EXPLAIN sql; EXPLAIN 分析的结果的表头如下: id | select_type | table | partitions | type | poss ...

  7. SQL语句性能分析常用命令

    DBCC freeproccache DBCC dropcleanbuffers 1.set statistics IO {ON| OFF} /*Transact-SQL 语句生成的磁盘活动量的信息* ...

  8. 查看sql语句加锁信息

    问题: 最近使用quartz集群,总是报deadlock问题,所以需要查看一下执行的sql导致的加锁冲突. 步骤: 1.在要测试的库中创建指定表innodb_lock_monitor create t ...

  9. 历史执行Sql语句性能分析 CPU资源占用时间分析

    SELECT     HIGHEST_CPU_QUERIES.PLAN_HANDLE,     HIGHEST_CPU_QUERIES.TOTAL_WORKER_TIME,     Q.DBID,   ...

随机推荐

  1. Python第四章-流程控制

    流程控制 在以前的代码中,所有的代码都是交由 Python 忠实地从头执行到结束.但是这些远远不够.很多时候需要根据不同的情况执行不同的代码. 如果你想改变这一工作流程,应该怎么做? 就像这样的情况: ...

  2. Rasa Stack:创建支持上下文的人工智能助理和聊天机器人教程

    相关概念 Rasa Stack 是一组开放源码机器学习工具,供开发人员创建支持上下文的人工智能助理和聊天机器人: • Core = 聊天机器人框架包含基于机器学习的对话管理 • NLU = 用于自然语 ...

  3. POJ 3461 Oulipo KMP算法(模板)

    题意: 给两组字符串a和b,求a在b中出现的次数 关于KMP: 马拉车算法是处理回文串,而KMP是处理前后缀的相同字符串的最长长度. a | a | b | a | a | f | a | a 数组 ...

  4. 字节码类库之Javassist

    Javassist优势 – 比反射开销小,性能高.–javassist性能高于反射,低于ASM运行时操作字节码可以让我们实现如下功能:– 动态生成 新的类– 动态改变某个类的结构 ( 添加 / 删除 ...

  5. docker下centos7编译安装ffmpeg

    1.安装基础命令 docker下精简版centos没有make等命令,先安装: yum -y install gcc automake autoconf libtool make yum instal ...

  6. 【PHP源码】PHP 函数调用

    title: [PHP 源码]PHP 函数调用 date: 2020-03-30 23:25:00 updated: 2020-04-04 19:57:00 tags: PHP 源码 想法 我以前对于 ...

  7. 1030 Travel Plan (30分)(dijkstra 具有多种决定因素)

    A traveler's map gives the distances between cities along the highways, together with the cost of ea ...

  8. LinkedHashMap 与 HashMap 实现的区别

    阅读前最好对 HashMap 的内部实现方式有一定了解 LinkedHashMap 继承自 HashMap 主要重写了一个节点类 LinkedHashMap.Entry,并维护一个头结点和尾节点 以及 ...

  9. Nginx知多少系列之(二)安装

    目录 1.前言 2.安装 3.配置文件详解 4.Linux下托管.NET Core项目 5.Linux下.NET Core项目负载均衡 6.Linux下.NET Core项目Nginx+Keepali ...

  10. JMeter 接口测试 自动生成签名机制

    在进行接口测试时,遇到接口进行了签名校验,为实现自动生成签名,经过一点研究终于成功. 首先,需要从前端获取 签名加密包  XXXsign.jar..  建议将该jar包放在 jmeter lib 目录 ...