备注:上接 MySQL 加锁处理分析(二)html
写到这里,其实MySQL的加锁实现也已经介绍的八八九九。只要将本文上面的分析思路,大部分的SQL,都能分析出其会加哪些锁。而这里,再来看一个稍微复杂点的SQL,用于说明MySQL加锁的另一个逻辑。SQL用例以下:mysql
如图中的SQL,会加什么锁?假定在Repeatable Read隔离级别下 (Read Committed隔离级别下的加锁状况,留给读者分析。),同时,假设SQL走的是idx_t1_pu索引。sql
在详细分析这条SQL的加锁状况前,还须要有一个知识储备,那就是一个SQL中的where条件如何拆分?具体的介绍,建议阅读我以前的一篇文章:SQL中的where条件,在数据库中提取与应用浅析 。在这里,我直接给出分析后的结果:数据库
Index key:pubtime > 1 and puptime < 20。此条件,用于肯定SQL在idx_t1_pu索引上的查询范围。并发
Index Filter:userid = ‘hdc’ 。此条件,能够在idx_t1_pu索引上进行过滤,但不属于Index Key。spa
Table Filter:comment is not NULL。此条件,在idx_t1_pu索引上没法过滤,只能在聚簇索引上过滤。.net
在分析出SQL where条件的构成以后,再来看看这条SQL的加锁状况 (RR隔离级别),以下图所示:htm
从图中能够看出,在Repeatable Read隔离级别下,由Index Key所肯定的范围,被加上了GAP锁;Index Filter锁给定的条件 (userid = ‘hdc’)什么时候过滤,视MySQL的版本而定,在MySQL 5.6版本以前,不支持Index Condition Pushdown(ICP), 所以Index Filter在MySQL Server层过滤,在5.6后支持了Index Condition Pushdown,则在index上过滤。若不支持ICP,不知足Index Filter的记录,也须要加上记录X锁,若支持ICP,则不知足Index Filter的记录,无需加记录X锁 (图中,用红色箭头标出的X锁,是否要加,视是否支持ICP而定);而Table Filter对应的过滤条件,则在聚簇索引中读取后,在MySQL Server层面过滤,所以聚簇索引上也须要X锁。最后,选取出了一条知足条件的记录[8,hdc,d,5,good],可是加锁的数量,要远远大于知足 条件的记录数量。blog
结论:在Repeatable Read隔离级别下,针对一个复杂的SQL,首先须要提取其where条件。Index Key肯定的范围,须要加上GAP锁;Index Filter过滤条件,视MySQL版本是否支持ICP,若支持ICP,则不知足Index Filter的记录,不加X锁,不然须要X锁;Table Filter过滤条件,不管是否知足,都须要加X锁。索引
本文前面的部分,基本上已经涵盖了MySQL/InnoDB全部的加锁规则。深刻理解MySQL如何加锁,有两个比较重要的做用:
能够根据MySQL的加锁规则,写出不会发生死锁的SQL;
能够根据MySQL的加锁规则,定位出线上产生死锁的缘由;
下面,来看看两个死锁的例子 (一个是两个Session的两条SQL产生死锁;另外一个是两个Session的一条SQL,产生死锁):
上面的两个死锁用例。第一个很是好理解,也是最多见的死锁,每一个事务执行两条SQL,分别持有了一把锁,而后加另外一把锁,产生死锁。
第二个用例,虽然每一个Session都只有一条语句,仍旧会产生死锁。要分析这个死锁,首先必须用到本文前面提到的MySQL加锁的规则。针对 Session 1,从name索引出发,读到的[hdc, 1],[hdc, 6]均知足条件,不只会加name索引上的记录X锁,并且会加聚簇索引上的记录X锁,加锁顺序为先[1,hdc,100],后[6,hdc,10]。而 Session 2,从pubtime索引出发,[10,6],[100,1]均知足过滤条件,一样也会加聚簇索引上的记录X锁,加锁顺序为[6,hdc,10],后 [1,hdc,100]。发现没有,跟Session 1的加锁顺序正好相反,若是两个Session刚好都持有了第一把锁,请求加第二把锁,死锁就发生了。
结论:死锁的发生与否,并不在于事务中有多少条SQL语句,死锁的关键在于:两个(或以上)的Session加锁的顺序不一致。而使用本文上面提到的,分析MySQL每条SQL语句的加锁规则,分析出每条语句的加锁顺序,而后检查多个并发SQL间是否存在以相反的顺序加锁的状况,就能够分析出各类潜在的死锁状况,也能够分析出线上死锁发生的缘由。
写到这儿,本文也告一段落,作一个简单的总结,要作的彻底掌握MySQL/InnoDB的加锁规则,甚至是其余任何数据库的加锁规则,须要具有如下的一些知识点:
了解数据库的一些基本理论知识:数据的存储格式 (堆组织表 vs 聚簇索引表);并发控制协议 (MVCC vs Lock-Based CC);Two-Phase Locking;数据库的隔离级别定义 (Isolation Level);
了解SQL自己的执行计划 (主键扫描 vs 惟一键扫描 vs 范围扫描 vs 全表扫描);
了解数据库自己的一些实现细节 (过滤条件提取;Index Condition Pushdown;Semi-Consistent Read);
了解死锁产生的缘由及分析的方法 (加锁顺序不一致;分析每一个SQL的加锁顺序)
有了这些知识点,再加上适当的实战经验,全面掌控MySQL/InnoDB的加锁规则,当不在话下。