前言
本文所说的 MySQL 事务都是指在 InnoDB
引擎下,MyISAM 引擎是不支持事务的。
读未提交和串行化基本上是不需要考虑的隔离级别,前者不加锁限制。串行化相当于单线程执行,效率太差。
读提交解决了脏读问题,行锁解决了并发更新的问题。并且 MySQL 在可重复读级别解决了幻读问题,是通过行锁和间隙锁的组合 Next-Key 锁实现的。
参考文献
事务4大隔离级别(思维导图):https://kdocs.cn/l/ssTeFXsem1JU
基础概念
事务隔离级别产生的问题对比
事务隔离级别 | 脏读 | 不可重复读 | 幻读 | 加锁读 |
---|---|---|---|---|
读未提交(read-uncommitted) | 是 | 是 | 是 | 否 |
不可重复读(read-committed) | 否 | 是 | 是 | 否 |
可重复读(repeatable-read) | 否 | 否 | 是 | 否 |
串行化(serializable) | 否 | 否 | 否 | 是 |
产生的问题名次解释
1、脏读
事务A读到了事务B还没有提交的数据
比如银行取钱,事务A开启事务,此时切换到事务B,事务B开启事务–>取走100元,此时切换回事务A,事务A读取的肯定是数据库里面的原始数据,因为事务B取走了100块钱,并没有提交,数据库里面的账务余额肯定还是原始余额,这就是脏读。
2、不可重复读
在一个事务里面读取了两次某个数据,读出来的数据不一致
还是以银行取钱为例,事务A开启事务–>查出银行卡余额为1000元,此时切换到事务B事务B开启事务–>事务B取走100元–>提交,数据库里面余额变为900元,此时切换回事务A,事务A再查一次查出账户余额为900元,这样对事务A而言,在同一个事务内两次读取账户余额数据不一致,这就是不可重复读。
3、幻读
在一个事务里面的操作中发现了未被操作的数据
比如学生信息,事务A开启事务–>修改所有学生当天签到状况为false,此时切换到事务B,事务B开启事务–>事务B插入了一条学生数据,此时切换回事务A,事务A提交的时候发现了一条自己没有修改过的数据,这就是幻读,就好像发生了幻觉一样。幻读出现的前提是并发的事务中有事务发生了插入、删除操作。
事务的隔离级别示例:
读未提交(READ_UNCOMMITTED)
即能够读取到没有被提交的数据
举例说明:
启动两个事务,分别为事务A和事务B。在事务A中使用 update 语句,修改 age 的值为10,初始是1。在执行完 update 语句之后。在事务B中查询 user 表,会看到 age 的值已经是 10 了,这时候事务A还没有提交(commit)。
在事务B进行操作的过程中,很有可能事务A由于某些原因,进行了事务回滚操作,那其实事务B得到的就是脏数据了,拿着脏数据去进行其他的计算,那结果肯定也是有问题的
顺着时间轴往表示两事务中操作的执行顺序,重点看图中 age
字段的值。
读已提交(READ_COMMITED )
在一个事务里面读取了两次某个数据,读出来的数据不一致
举例说明:
启动两个事务,分别为事务A和事务B。在事务A中使用 update 语句将 id=1 的记录行 age 字段改为 10。此时,在事务B中使用 select 语句进行查询,我们发现在事务A提交之前,事务B中查询到的记录 age 一直是1,直到事务A提交,此时在事务B中 select 查询,发现 age 的值已经是 10 了。
这就出现了一个问题,在同一事务中(本例中的事务B),事务的不同时刻同样的查询条件,查询出来的记录内容是不一样的,事务A的提交影响了事务B的查询结果,这就是不可重复读,也就是读提交隔离级别。
每个 select 语句都有自己的一份快照,而不是一个事务一份,所以在不同的时刻,查询出来的数据可能是不一致的。
读提交解决了脏读的问题,但是无法做到可重复读,也没办法解决幻读。
可重复读取(REPEATABLE_READ)
在一个事务里面的操作中发现了未被操作的数据
举例说明:
RR 隔离级别下,如果只是读的话,不会产生脏读、不可重复读、幻读。如果事务中有写
的话,则会产生幻读
问题。下面,我通过两个案例,对读和写,进行隔离级别的分析 。
读 Read
事务A
启动后修改了数据,并且在事务B
之前提交。事务B
在事务开始和事务A
提交之后两个时间节点都读取的数据相同,已经可以看出可重复读的效果。
写 Write (幻读产生过程)
事务A
开始后,执行 update
操作,将 age = 1
的记录的 name
改为 “风筝2号”
;
事务B
开始后,在事务执行完 update
后,执行 insert
操作,插入记录 age =1,name = 古时的风筝
。
这和事务A修改的那条记录值相同,然后提交。
事务B
提交后,事务A
中执行 select
,查询 age=1
的数据。
这时会发现多了一行,并且发现还有一条 name = 古时的风筝,age = 1
的记录,这其实就是事务B
刚刚插入的,这就是幻读
。
需要说明的是:当你在 MySQL 中测试幻读的时候,并不会出现图中的结果,幻读并没有发生,MySQL 的可重复读(RR)隔离级别其实解决了幻读问题(可阅读MVCC相关文章)
串行化(SERLALIZABLE)
最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了
但是效果最差,它将事务的执行变为顺序执行,与其他三个隔离级别相比,它就相当于单线程,后一个事务的执行必须等待前一个事务结束。