MySQL选用可重复读之前一定要想到的事情
原文地址:http://blog.itpub.net/29254281/viewspace-1398273/
MySQL选用可重复读隔离级别之前一定要想到的事情.
间隙锁
MySQL在使用之前有三个务必要知道..(随着了解的深入这个极有可能再增加..)
1.DDL会引起metadata lock,导致请求连环阻塞,甚至是查询请求.
http://blog.itpub.net/29254281/viewspace-1383193/
2.MySQLDump和XtraBackup的flush table命令会引起waiting for table连环阻塞,同样也会阻塞查询请求.
http://blog.itpub.net/29254281/viewspace-1392757/
3.选用可重复读事务隔离级别,会开启间隙锁.他锁定的内容比实际需要的要多,并且很可能导致死锁.
本文是何登成大神文章的读后感和总结.
链接如下:
http://hedengcheng.com/?p=771
(关于这方面最好的文章,没有之一)
Oracle和MySQL(读提交和可重复读)都实现了MVCC多版本并发控制.
这意味着普通的Select(一致性读或者说快照读)可以以非锁定的形式读取数据.
而当前读(oracle的db block gets)都需要加锁.
比如:
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;
除了第一个SQL对记录上共享锁,其余都是排他锁.
MySQL虽然在可重复读事务隔离级别实现了避免幻读.
但是可重复读隔离级别的当前读,会启动间隙锁.
当前读(锁定读)加锁情况.
| 读提交 | 可重复读 | |
| 主键索引 | 锁定主键索引 | 锁定主键索引 |
| 唯一索引 | 锁定唯一索引的值和主键索引的值 | 锁定唯一索引的值和主键索引的值 |
| 普通索引 | 锁定普通索引的值和主键索引的值 | 锁定普通索引的值和主键索引的值,普通索引增加间隙锁 |
| 无索引 | 锁定所有的主键索引,在服务器层对不符合条件的记录解锁 | 锁定所有的主键索引和主键索引的间隙 |
根据何老师的例子(另记一份,现在的网站也是说没就没)
查看同样的SQL
(delete from t1 where id = 10;)
在不同的情况下,锁定的情况.
首先是读提交事务隔离级别,
如果id是主键索引,锁定主键索引的键值
如果id是唯一索引,锁定唯一索引和主键索引的相关键值.
如果id是普通索引,则锁定普通索引和主键索引的相关键值.
最后,如果id没有索引,在InnoDB层,会对所有主键索引上排他锁,到MySQL服务器层对不符合条件的记录进行解锁.
而在可重复读事务隔离级别,
如果id是主键索引,和读提交一样,会锁定主键索引的相关键值.
如果id是唯一索引,MySQL会将间隙锁退化为行级锁,仅仅锁定唯一索引和主键索引的相关键值.同读提交隔离级别一样.
例外:
如果是组合的唯一索引 create unique index inx_1 on test(a,b,c);
但是查询只用到了a,b select * from test where a=? and b=? for update
MySQL这时不会退化为行级锁,他的处理方式会等同于下面说到的普通索引.(行级锁+间隙锁)
如果id是普通索引,MySQL会锁定普通索引和主键索引的相关键值,并且锁定相关普通索引之间的间隙.
以上图为例,select * from t1 where id=100 for update;
这个SQL没有查到任何的记录,但是他同样会上间隙锁.
(在这种情况下,即使id是主键索引或者唯一索引,也会产生间隙锁)
在可重复读的隔离级别下,如果id没有索引
他会锁定主键索引的所有记录和所有间隙.
和读提交不一样,在MySQL服务器层,并不会对不符合条件的记录解锁.并且它会锁定主键索引的所有间隙.
参考何老师文章中的一个例子
在可重复读隔离级别下,
Index key:pubtime > 1 and puptime < 20。此条件,用于确定SQL在idx_t1_pu索引上的查询范围。
Index Filter:userid = ‘hdc’ 。此条件,可以在idx_t1_pu索引上进行过滤,但不属于Index Key。
Table Filter:comment is not NULL。此条件,在idx_t1_pu索引上无法过滤,只能在聚簇索引上过滤。
他的加锁情况如下
从图中可以看出,在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],但是加锁的数量,要远远大于满足条件的记录数量。
结论:在Repeatable Read隔离级别下,针对一个复杂的SQL,首先需要提取其where条件。Index Key确定的范围,需要加上GAP锁;Index Filter过滤条件,视MySQL版本是否支持ICP,若支持ICP,则不满足Index Filter的记录,不加X锁,否则需要X锁;Table Filter过滤条件,无论是否满足,都需要加X锁。
以上是对何老师文章的总结.
这个文章看了很长时间,但是有一个问题一直很疑惑.
实验环境
实验数据
create table t
(
a int primary key,
b int,
c int,
key (b,c)
) engine=innodb;
insert into t
values
(1,10,10),
(3,10,20),
(5,20,30),
(7,20,40),
(9,20,50);
commit;
终端一:
select * from t where b=10 and c=10 for update;
理论上,他会锁定 (b=10 c=10)的辅助索引,(a=1)的主键索引并且会锁定(10,负无穷)至(10,10),(10,10)至(10,20)的范围,也就是间隙锁.
但是终端二:
select * from t where b=10 and c=15 for update;
查询并没有阻塞.这不对啊.因为在这个隔离级别下,即使没有数据,也会产生间隙锁.
终端三:
回滚终端二,在终端三输入
insert into t values(50,10,15);
我发现这个SQL被阻塞了,也就是说明终端一的间隙锁是存在的.
这个问题查阅了官方文档,
http://dev.mysql.com/doc/refman/5.6/en/innodb-record-level-locks.html
但其实没有看懂..
在缺乏理论依据的基础上,
我猜测虽然当前读的行锁是排他的(除了那个lock in share mode).
但是间隙锁有共享锁的性质,一旦出现间隙锁,在这个间隙的范围内不能新出现任何记录
无论是update修改过来的,还是insert新增过来的
只要间隙内不出现新的记录,间隙锁之间就可以共存.
参考:
开启锁监控,可以在show engine innodb status\G看到更多锁信息,并且定时将信息写入错误日志.
create table innodb_lock_monitor(a int) engine=innodb;
查看ICP是否开启
select @@optimizer_switch\G
MySQL选用可重复读之前一定要想到的事情的更多相关文章
- MySQL使用可重复读作为默认隔离级别的原因
一般的DBMS系统,默认都会使用读提交(Read-Comitted,RC)作为默认隔离级别,如Oracle.SQL Server等,而MySQL却使用可重复读(Read-Repeatable,RR). ...
- MySQL的可重复读级别能解决幻读吗
引言 之前在深入了解数据库理论的时候,了解到事物的不同隔离级别可能存在的问题.为了更好的理解所以在MySQL数据库中测试复现这些问题.关于脏读和不可重复读在相应的隔离级别下都很容易的复现了.但是对于幻 ...
- 【MySQL】可重复读模式下 unique key失效案例
一 [背景] 今天上午文能提笔安天下,武能上马定乾坤的登博给团队出了一道题目,谁先复现问题,奖励星巴克一杯.激起了一群忙碌的屌丝DBA的极大热情.问题是这样滴,如下图登博提示了几个细节: 1. ...
- MySQL的可重复读级别能解决幻读问题吗?
之前在深入了解数据库理论的时候,了解到事务的不同隔离级别可能存在的问题.为了更好的理解所以在MySQL数据库中测试复现这些问题.关于脏读和不可重复读在相应的隔离级别下都很容易的复现了. 但是对于幻读, ...
- 面试官:MySQL的可重复读级别能解决幻读问题吗?
引言 之前在深入了解数据库理论的时候,了解到事务的不同隔离级别可能存在的问题.为了更好的理解所以在MySQL数据库中测试复现这些问题.关于脏读和不可重复读在相应的隔离级别下都很容易的复现了. 但是对于 ...
- Mysql可重复读原理
mysql可重复读现象及原理分析 InnoDB---可重复读隔离级别的底层实现原理 概念 可重复读的实现 Repeatable Read(可重复读):一个事务在执行过程中可以看到其他事务已经提交的新插 ...
- MySQL Transaction--MySQL与SQL Server在可重复读事务隔离级别上的差异
MySQL和SQL Server两种数据库在REPEATABLE-READ事务隔离级别实现方式不同,导致使用上也存在差异. 在MySQL中,默认使用REPEATABLE-READ事务隔离级别,MySQ ...
- mysql系列:加深对脏读、脏写、可重复读、幻读的理解
关于相关术语的专业解释,请自行百度了解,本文皆本人自己结合参考书和自己的理解所做的阐述,如有不严谨之处,还请多多指教. 事务有四种基本特性,叫ACID,它们分别是: Atomicity-原子性,Con ...
- mysql 可重复读
概念 Repeatable Read(可重复读):即:事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,那么事务A再读该数据,读到的还是原来的内容. 实现原理(MVCC [ 多版本并发控制 ...
随机推荐
- Java 406
项目改名之后, 项目上传后,报错,406,可是项目本地是可以跑起来的, 联系管理员,管理员改了个/etc/httpd/conf/workers2.properties 里面,将本次的项目加入进去就OK ...
- Axiom3D学习日记 2.介绍SceneManager,SceneNode,Entity
SceneManager(场景管理类) 所有出现在屏幕里的东西都受SceneManager管理(最好是这样),当你放置对象在场景里,SceneManager就会跟踪他们的位置,当你为场景创建一个相机, ...
- UTF-8和GBK有什么区别?
字符均使用双字节来表示,只不过为区分中文,将其最高位都定成1. 至于UTF-8编码则是用以解决国际上字符的一种多字节编码,它对英文使用8位(即一个字节),中文使用24位(三个字节)来编码.对于英文字符 ...
- 【转】IOS 30多个iOS常用动画,带详细注释
原文: http://blog.csdn.net/zhibudefeng/article/details/8691567 CoreAnimationEffect.h 文件 // CoreAnimati ...
- Swift 中 Selector 方法的访问权限控制问题
今天用Swift写了个视图,在视图上加个手势,如下所示: panGestureRecognizer = UIPanGestureRecognizer(target: self, action: &qu ...
- UIDatePikcer的基本用法
- (void)viewDidLoad { [super viewDidLoad]; _datePicker = [[UIDatePicker alloc] initWithFrame:CGRectM ...
- SVN Global ignore pattern 忽略文件正则后缀
*.o *.lo *.la *.al .libs *.so *.so.[0-9]* *.a *.pyc *.pyo __pycache__ *.rej *~ #*# .#* .*.swp .DS_St ...
- java_设计模式_模板方法模式_Template Method Pattern(2016-08-11)
定义: 定义一个操作中算法的骨架,而将一些步骤延迟到子类中,使得子类可以不改变算法的结构即可重定义该算法中的某些特定步骤.这里的算法的结构,可以理解为你根据需求设计出来的业务流程.特定的步骤就是指那些 ...
- SGU 125.Shtirlits
时间限制:0.25s 空间限制:4M 题意: 有N*N的矩阵(n<=3),对所有i,j<=n有G[i][j]<=9,定义f[i][j]为G[i][j]四周大于它的数的个数(F[i][ ...
- phpcms(3) V9 常用函数 及 代码整理(转)
转自http://www.cnblogs.com/Braveliu/p/5103918.html 常用函数 及 常用代码 总结如下 <;?php //转换字符串或者数组的编码 str_chars ...