读未提交
我们看到这时的隔离级别是读提交,那么我们就要把隔离级别改为,读未提交。
我们可以看到两个终端的mysql隔离级别已经都被改成了读未提交。
开始演示读未提交:
开启2个事务
事务2读取到了事务1还未提交的数据,这就是读未提交。
一个事务能读到另一个事务还未提交的数据这叫脏读。
读提交
我们现在的隔离级别是读未提交,那么我们把它更改为读提交
重启mysql客户端后我们就会看到,隔离级别修改成功。
开始演示读提交:
开启2个事务
1个事务结束提交了,另一个事务就能看到该已经结束事务修改的数据,如果该事务未结束就看不到叫做读提交。
所以在事务1提交之前,事务2的数据不是最新的。
在多个事务并发运行时,任何一个有读取条件的事务,它极有可能在其它事务未提交修改数据时看不到修改的数据,提交了自己未结束就可以看到修改的数据,就会导致同一个事务内部两次select可能看到的结果不一样。这种现象叫不可重复读。
那么不可重复读是问题吗?
举例子:
今年到年底了,老板准备给不同工资的员工发送不同的礼品。
老板把这个任务交代给了总经理。
于是总经理就计划出了下列的方案。
[1000,2000) 送水杯
[2000,3000) 送键盘
[3000,4000) 送微波炉
[4000,5000) 送电视
[5000,10000) 送手机
然后总经理让程序员小张,根据方案找出各个工资对应的人员。
于是小张就启动了一个事务
beigin;
select name from emp where
sal >=1000 and sal < 2000;
2000 3000
3000 4000
4000 5000
5000 10000
commit;
但是在小张运行事务的同时。
Tom觉得自己工资很低就3000块,但是自己对公司的贡献很大,于是Tom找到老板让老板给自己涨工资,老板一看确实Tom贡献很大,于是就让小王把Tom的工资涨到4500
于是小张就启动了一个事务。
begin;
update emp set sal = 4500 where name = ‘Tom’;
commit;
但是这时候可能就遇到一个情况:小张,小王的事务同时在运行。
小王还没改完,小张在3000-4000工资处查到了Tom。
小王改完了,小张刚好又在4000-5000工资处查到了Tom。
所以这里就出现了问题,小张可能会疑惑,Tom的工资怎么既在3000-4000,又在4000-5000呢?
所以不可重复读是问题。
可重复读
我们初始的隔离级别是读提交,那么接下来我们要把它改成可重复读。
重启mysql客户端
我们看到修改成功了。
开始演示可重复读:
开启2个事务
重启一个事务
我们发现,确实在事务重启后就能看见修改的数据内容。
再次演示可重复读:
开启事务
事务2原始数据:
事务1修改数据:
事务2查看数据:
结束事务2
重启一个事务
多次查看,发现终端A在对应事务中insert的数据,在终端B的事务周期中,也没有什么影响,也符合可重复的特点。但是,一般的数据库在可重复读情况的时候,无法屏蔽其他事务insert的数据(为什么?因为隔离性实现是对数据加锁完成的,而insert待插入的数据因为并不存在,那么一般加锁无法屏蔽这类问题),会造成虽然大部分内容是可重复读的,但是insert的数据在可重复读情况被读取出来,导致多次查找时,会多查找出来新的记录,就如同产生了幻觉。这种现象,叫做幻读(phantom read)。很明显,MySQL在RR级别的时候,是解决了幻读问题的(解决的方式是用Next-Key锁(GAP+行锁)解决的。这块比较难,有兴趣同学了解一下)
串行化
我们看到初始隔离级别是可重复读,那么把它改成串行化
修改成功!
演示串行化:
开启2个事务
事务1和事务2查看数据
事务1:删除数据
我们发现卡在这里
但是事务2还可以查数据。
最后我们发现事务1删数据超时了。
这时我们把事务2结束掉;
然后事务1再删除数据
我们这时就会发现,当只有事务1在运行的时候,对数据修改才会成功。
对所有操作全部加锁,进行串行化,不会有问题,但是只要串行化,效率很低,几乎完全不会被采用
总结
- 其中隔离级别越严格,安全性越高,但数据库的并发性能也就越低,往往需要在两者之间找一个平衡点。
- 不可重复读的重点是修改和删除:同样的条件, 你读取过的数据,再次读取出来发现值不一样了 。幻读的重点在于新增:同样的条件,第1次和第2次读出来的记录数不一样
- 说明: mysql 默认的隔离级别是可重复读,一般情况下不要修改
- 上面的例子可以看出,事务也有长短事务这样的概念。事务间互相影响,指的是事务在并行执行的时候,即都没有commit的时候,影响会比较大。