背景

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. 算法学习 八皇后问题的递归实现 java版 回溯思想

    1.问题描述 八皇后问题是一个以国际象棋为背景的问题:如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行.纵行或 ...

  2. SpringBoot项目中应用Jedis和一些常见配置

    优雅的使用Jedis Redis的Java客户端有很多,Jedis是其中使用比较广泛和性能比较稳定的一个.并且其API和RedisAPI命名风格类似,推荐大家使用 在项目中引入Jedis 可以通过Ma ...

  3. PyTorch ImageNet 基于预训练六大常用图片分类模型的实战

    微调 Torchvision 模型 在本教程中,我们将深入探讨如何对 torchvision 模型进行微调和特征提取,所有这些模型都已经预先在1000类的Imagenet数据集上训练完成.本教程将深入 ...

  4. coding++:RateLimiter 限流算法之漏桶算法、令牌桶算法--简介

    RateLimiter是Guava的concurrent包下的一个用于限制访问频率的类 <dependency> <groupId>com.google.guava</g ...

  5. 爬虫scrapy框架的使用

    第一步 下载scrapy模块: pip install scrapy 第二步 创建项目 在终端/cmd进入创建项目的目录:scrapy startproject douban(项目名) 导入pycha ...

  6. 来讨论一下这些常见的 Redis 面试题

    Redis应该算面试中必问的一个知识点,但是发现很多童鞋并不熟悉这块,这篇就常见的一些问题做一些整理,有不对的地方欢迎留言指正! 1.Redis支持的数据类型? String(字符串) 格式: set ...

  7. [codevs1036]商务旅行<LCA:tarjan&倍增>

    题目链接:http://codevs.cn/problem/1036/ 今天翻箱倒柜的把这题翻出来做了,以前做的时候没怎么理解,所以今天来重做一下 这题是一个LCA裸题,基本上就把另一道裸题小机房的树 ...

  8. js中写laravel模板blade语法和PHP逻辑解决方法

    在js中是否能够执行blade的语法?或者说在js中能否处理PHP逻辑呢? 答案是,当然的 下面来看需求:在提交表单,完成入库操作后,使用 return redirect(route('admin.u ...

  9. C/C++知识总结 三 C/C++数据类型与输入输出

    C/C++数据类型与输入输出 基本数据类型 输入与输出 复合数据类型(将在下几篇博客中总结) C/C++数据类型 数据类型总图 数据类型差别 数据类型不同的意义 1)指明数据的大小,以便正确分配,访问 ...

  10. Python 【基础面试题】

    前言 面试题仅做学习参考,学习者阅后也要用心钻研其中的原理,重要知识需要系统学习.透彻学习,形成自己的知识链.以下五点建议希望对您有帮助,早日拿到一份心仪的offer. 做好细节工作,细致的人运气不会 ...