mysql隔离级别在面试中经常出现,今天我就分析一下这几个隔离级别,mysql提供了四种隔离级别,以及解决可以解决哪种问题,如下图
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | 不能解决 | 不能解决 | 不能解决 |
读已提交 | 能解决 | 不能解决 | 不能解决 |
可重复读 | 能解决 | 能解决 | 能解决 |
串行化 | 能解决 | 能解决 | 能解决 |
特别说明,mysql默认隔离级别是可重复读,但是网上有很多说法可重复读不能解决幻读问题,但是mysql的可重复读是可以解决幻读的问题,后面会说到
首先我们明确几个概念
脏读:
有两个事务A,B,事务A仅仅是更新了某行数据,但是没有提交,但是事物B确读取到了事务A更新的数据,此时事务A有进行了回滚,就会导致事务B读取到错误的数据
不可重复读:有两个事务A,B,事务B开始读取某一行数据例如id=1,age=1,但是此时事务A更新了id=1,age=10,但是此时事务B再次读取发现id=1,age=10,即同一个事务不同时刻读取到了不同的数据
可重复读:
一个事物开始知道事务结束前读物的数据都是相同的
幻读:有两个事务A,B,但是事物B某时刻读取到id=1,age=1有一条数据,但是在第二次读取的时候,事务A,插入了id=1,age=10,然后事物B第二次读取的时候发现id=1居然有两条数据,是不是产生幻觉了
其次我们说明一下事务隔离级别
前提环境是数据库有一张表如下
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(30) DEFAULT NULL,
`age` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
insert into user('name','age') values ('张三',1);
读未提交:
事务是靠锁来时实现的,但是读未提交是没有使用锁的,因此他是性能最好的,他的含义如下图
事务A在更新一条数据之后,但是没有提交,而此时事务B读取到这条数据,此时事务A又进行了更新,然后事物B拿着错误数据操作业务
读已提交:
这个隔离级别解决了上面的脏读,具体如下图
两个事务A,B,在事务B中只能读取到事务A提交的数据
可重复读
就是在同一个事务中,事务开始和事务结束之前读取的的数据都是一致的
如下图
事务B在事务开始直到事务结束读取的数据都是一致的,不管事务是否提交.
mysql是如何实现可重读的的呢,也是面试中常问的事情,今天也说明一下
其实mysql是使用MVCC(多版本控制实现),在数据库看到的数据实际上可能存在多份数据,而这每一份数据,都会生成一个事物id,全局唯一的,正如下图,存在三个版本的数据,同时每一个事务开始都是会生成一个快照,即一致性视图,可重读即事务开始产生一个全局事务快照,而读已提交每次执行都会产生一个新快照
重点是快照的读取规则
串行化
串行化是这里面最厉害的一种隔离级别,他把所有事务都搞成了顺序执行,就行但是单线程执行了,性能很差
幻读
这里说明一下幻读的含义,如下图
在事物A中,开始读取id=1,只有一条数据,但是在第二次读取之前,事务B插入了一条数据同样是id=1,然后事物A再次读取的时候就是发现有两条数据了
最后注重说明一下如何解决幻读问题
我们知道在高并发环境中,可能存在这种情况,两个事务A,B,对同一条数据进行修改,谁最后执行就按照谁的结果处理,并且我们知道事务更新之前先要读取数据,而这个读取数据,是当前读,即在读取多版本哪种最新的那一条数据
如下图
事务A,在更新id=1的时候,此时事务B也要更新id=1,但是此时事务会给id=1加上行锁,此时事务B只能等待,直到事务A提交释放,事务B才会执行,
而我们mysql使用的重复读隔离级别是使用行锁和间隙锁结合的next-key锁解决的幻读
如上图,假设数据有两条数据
id | name | age |
---|---|---|
1 | 张三 | 10 |
2 | 李四 | 30 |
然后我们按照下面逻辑执行
当时事务A执行update user set name='张思' where age=10的时候在age=10加入了行锁并且加入了间隙锁(10,30](负无穷,10],因此事务B在执行插入的时候就会被阻塞,直到事务A被提交释放才会执行。
由于age是有索引的,此时只有age>10的数据不会被阻塞,当age没有索引的时候,就会个整个表加上间隙锁和行锁,导致整个表插入数据都会被阻塞,直到事务A提交释放