数据库的事务对数据并行访问的时候,有可能会出现一些问题,因此数据库设置了四个不同的隔离级别来解决问题。
在 MySQL 数据库的隔离级别可以分为四层,分别是读未提交、读提交、可重复读和串行化。与之对应出现的问题有脏读、幻读、不可重复读。
<!--more-->
隔离级别
读未提交(read uncommited)
一个事务还未提交时,它做的变更就能被其他的事务看到。
读提交(read commited)
一个事务提交之后,它做的变更才会被其他事务看到
可重复读(repeatable read)
一个事务执行过程中看到的数据,总是跟这个事务启动时看到的数据是一致的。在此隔离级别下,未提交的事务对其他事务也是不可见的。
串行化(serializable)
对同一行记录,“写” 会加 “写锁”,“读” 会加 “读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。
此隔离级别保证了数据的正确性,但是频繁的加锁会对性能造成严重的影响。
不同的事务隔离级别对应的问题
脏读
在 “读未提交” 级别下,未提交的变更被其他事务看到了,对其他事务来说,这些未提交的变更就是脏数据,这样的现象就称之为“脏读”。
不可重复读
在 “读提交” 级别下,只有事务提交之后,别的事务才能看到事务做的更改,但是当一个事务 A 在执行过程中多次查询某个值,第一次查询的时候值为 v1, 然后在事务 B 把值修改了,当事务 A 再次查询的时候,就会发现这个值不一样了,明明是同一个事务进行的查询操作,两次查询出来的结果却不同,这就是“不可重复读”
幻读
“幻读” 与 “不可重复读” 比较类似,事务 A 查询某个值,如果不存在,就插入,当事务 A 查询到不存在之后,事务 B 插入了这个值,事务 A 再去插入的时候就会发现之前查询不存在的值居然有了。解决幻读的方法就是设置事务隔离级别为“串行化”,即无论读写,都对数据加锁。
用下面这个过程来展示一下区别
事务A | 事务B |
---|---|
启动事务A, 查询得到值1 | 启动事务B |
查询得到值1 | |
将1改为2 | |
查询得到值 v1 | |
提交事务B | |
查询得到值 v2 | |
提交事务 A | |
查询得到值 v3 |
- 若隔离级别是“读未提交”,则 v1 的值就是2.这时候事务 B 虽然还没有提交,但是结果已经被 A 看到了。因此, v2、v3 都是 2。
- 若隔离级别是“读提交”,则 v1 是 1, v2 的值是 2.事务 B 的更新在提交后才能被 A 看到。所以, v3 的值也是 2.
- 若隔离级别是“可重复读”,则 v1、v2 是 1,v3 是 2。在“可重复读”级别下需要遵循这个要求:事务在执行期间看到的数据前后必须都是一直的。
- 若隔离级别是“串行化”,则在事务 B 执行“将1改为2”的时候,会被锁住。知道事务 A 提交之后,事务 B 才可以继续执行。所以从 A 的角度看,v1、v2的值是 1,v3 的值是 2.
在事务的隔离级别实现上,数据库里面会创建一个视图,访问的时候以视图的逻辑结果为准。
- 在“可重复读”隔离级别下,这个视图是在事务启动时创建的,整个事务执行期间都使用这个视图;
- 在“读提交”级别下,这个视图是在每个 SQL 语句开始执行的时候创建的;
- 如果是“读未提交”级别,直接返回记录上的最新值,没有视图概念;而在“串行化”级别下直接用加锁的方式来避免并行访问。
公众号:没有梦想的阿巧 后台回复 "群聊",一起学习,一起进步
有疑问加站长微信联系(非本文作者)