您的位置:首页 > 数据库 > MySQL

新说Mysql事务隔离级别

2018-12-06 16:00 676 查看

引言

大家在面试中一定碰到过

说说事务的隔离级别吧?

老实说,事务隔离级别这个问题,无论是校招还是社招,面试官都爱问!然而目前网上很多文章,说句实在话啊,我看了后我都怀疑作者弄懂没!因为他们对可重复读(Repeatable Read)和串行化(serializable)的解析实在是看的我一头雾水!
再加上很多书都说可重复读解决了幻读问题,比如《mysql技术内幕--innodb存储引擎》等,不一一列举了,因此网上关于事务隔离级别的文章大多是有问题的,所以再开一文说明!
本文所讲大部分内容,皆有官网作为佐证,因此对本文内容你可以看完后,你完全可以当概念记在脑海里,除非官网的开发手册是错的,否则应当无误!
另外,本文会重点说一下

可重复读(Repeatable Read)是否真的解决幻读的问题!

正文

开始我先提一下,根据事务的隔离级别不同,会有三种情况发生。即脏读、不可重复读、幻读。这里我先不提这三种情况的定义,后面在讲隔离级别的时候会补上。
这里,大家记住一点,根据脏读、不可重复读、幻读定义来看(自己总结,官网没有),有如下包含关系:

那么,这张图怎么理解呢?
即,如果发生了脏读,那么不可重复读和幻读是一定发生的。因为拿脏读的现象,用不可重复读,幻读的定义也能解释的通。但是反过来,拿不可重复读的现象,用脏读的定义就不一定解释的通了!

假设有表

tx_tb
如下,pId为主键

pIdname
1zhangsan

1、读未提交(READ_UNCOMMITTED)

其实这个从隔离名字就可以看出来,一个事务可以读到另一个事务未提交的数据!为了便于说明,我简单的画图说明!

如图所示,一个事务检索的数据被另一个未提交的事务给修改了。
官网对脏读定义的地址为

https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_dirty_read

其内容为

dirty read
An operation that retrieves unreliable data, data that was updated by another transaction but not yet committed.

翻译过来就是

检索操作出来的数据是不可靠的,是可以被另一个未提交的事务修改的!

你会发现,我们的演示结果和官网对脏读的定义一致。根据我们最开始的推理,如果存在脏读,那么不可重复读和幻读一定是存在的。

2、读已提交(READ_COMMITTED)

这个也能看的出来,一个事务能读到另一个事务已提交的数据!为了便于说明,我简单的画图说明!

如图所示,一个事务检索的数据只能被另一个已提交的事务修改。
官网对不可重复读定义的地址为

https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_non_repeatable_read

其内容为

non-repeatable read
The situation when a query retrieves data, and a later query within the same transaction retrieves what should be the same data, but the queries return different results (chan 16c8 ged by another transaction committing in the meantime).

翻译过来就是

一个查询语句检索数据,随后又有一个查询语句在同一个事务中检索数据,两个数据应该是一样的,但是实际情况返回了不同的结果。!

ps
:作者注,这里的不同结果,指的是在行不变的情况下(专业点说,主键索引没变),主键索引指向的磁盘上的数据内容变了。如果主键索引变了,比如新增一条数据或者删除一条数据,就不是不可重复读。

显然,我们这个现象符合不可重复读的定义。下面,大家做一个思考:

  • 这个不可重复读的定义,放到脏读的现象里是不是也可以说的通。显然脏读的现象,也就是读未提交(READ_UNCOMMITTED)的那个例子,是不是也符合在同一个事务中返回了不同结果!

  • 但是反过来就不一定通了,一个事务A中查询两次的结果在被另一个事务B改变的情况下,如果事务B未提交就改变了事务A的结果,就属于脏读,也属于不可重复读。如果该事务B提交了才改变事务A的结果,就不属于脏读,但属于不可重复读。

3、可重复读(REPEATABLE_READ)

这里,我改变一下顺序,先上幻读的定义
官网对幻读定义的地址为

https://dev.mysql.com/doc/refman/5.7/en/glossary.html#glos_phantom

phantom
A row that appears in the result set of a query, but not in the result set of an earlier query. For example, if a query is run twice within a transaction, and in the meantime, another transaction commits after inserting a new row or updating a row so that it matches the WHERE clause of the query.

翻译过来就是

在一次查询的结果集里出现了某一行数据,但是该数据并未出现在更早的查询结果集里。例如,在一次事务里进行了两次查询,同时另一个事务插入某一行或更新某一行数据后(该数据符合查询语句里where后的条件),并提交了!

好了,接下来上图,大家自己评定该现象是否符合幻读的定义

显然,该现象是符合幻读的定义的。同一事务的两次相同查询出现不同行。下面,大家做一个思考:

  • 这个幻读的定义,放到不可重复读的现象里是不是也可以说的通。大家自行思考!

  • 反过来就不一定通了。事务第二次查询出了一个数据,但是该数据并未出现在第一次查询的结果集里。如果该数据是修改数据,那么该现象既属于不可重复读,也属于幻读。如果该数据是新增或删除的数据,那该现象就不属于不可重复读,但属于幻读。

接下来说一下,为什么很多文章都产生误传,说是可重复读可以解决幻读问题!原因出自官网的一句话
(地址是:

https://dev.mysql.com/doc/refman/5.7/en/innodb-locking.html#innodb-record-locks
)
原文内容如下

By default, InnoDB operates in REPEATABLE READ transaction isolation level. In this case, InnoDB uses next-key locks for searches and index scans, which prevents phantom rows (see Section 14.7.4, “Phantom Rows”).

按照原本这句话的意思,应该是
InnoDB默认用了REPEATABLE READ。在这种情况下,使用next-key locks解决幻读问题!
结果估计,某个国内翻译人员翻着翻着变成了
InnoDB默认用了REPEATABLE READ。在这种情况下,可以解决幻读问题!
然后大家继续你抄我,我抄你,结果你懂的!
显然,漏了"使用了next-key locks!"这个条件后,意思完全改变,我们在该隔离级别下执行语句

select *  from tx_tb where pId >= 1;

是快照读,是不加任何锁的,根本不能解决幻读问题,除非你用

select *  from tx_tb where pId >= 1 lock in share mode;

这样,你就用上了next-key locks,解决了幻读问题!

4、串行读(SERIALIZABLE_READ)

在该隔离级别下,所有的

select
语句后都自动加上
lock in share mode
。因此,在该隔离级别下,无论你如何进行查询,都会使用
next-key locks
。所有的
select
操作均为当前读!

OK,注意看上表红色部分!就是因为使用了
next-key locks
,innodb将PiD=1这条索引记录,和(1,++∞)这个间隙锁住了。其他事务要在这个间隙上插数据,就会阻塞,从而防止幻读发生!
有的人会说,你这第二次查询的结果,也变了啊,明显和第一次查询结果不一样啊?对此,我只能说,请看清楚啊。这是被自己的事务改的,不是被其他事物修改的。这不算是幻读,也不是不可重复读。

总结

上面罗里吧嗦一大堆,最后来一个表格做总结吧,你面试答这个表就行。上面的一切是为了这张表做准备!

隔离级别脏读不可重复读幻读
读未提交
不可重复读
可重复读
串行化


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  MySQL