MySQL事务隔离性深度解析:四种隔离级别详解与实验演示
MySQL事务隔离性深度解析:四种隔离级别详解与实验演示
事务的隔离性是数据库系统中一个非常重要的概念,它决定了多个事务在并发执行时如何相互影响。本文将深入讲解事务的四种隔离级别:读未提交、读提交、可重复读和串行化,通过具体实验帮助读者理解每个隔离级别的特点和应用场景。
事务的隔离性
要理解隔离性,我们首先要明白什么是隔离性,什么是隔离级别,为什么会有这么多隔离级别。
首先,MySQL在启动后,一定会被多个客户端并发访问。我们可以向MySQL同时发送多个事务,对于上层应用来说,这些事务一定有执行前、执行中和执行后。一旦执行中出现问题,就会发生回滚。所以单个事务,对用户表现出来的特性,就是原子性。
而且,多个事务执行的时候,可能会互相影响。比如同时访问一张表,同时修改一张表。因此,在数据库中,为了保证事务之间尽量互不干扰,就有了一个重要的特性——隔离性。
数据库中,允许事务收到不同程度的干扰,这个就叫做隔离级别。
让我们用一个故事来说明数据的隔离性。假设两个人都在同时访问数据库。这两个人都开启了事务。但是一个人想要update,一个人想要select。那么请问是update先跑呢,还是select先跑呢?有人可能会说让update先跑,因为我们要确定我们看到的是最新的。这种说法是错误的。
为什么错误呢?可以这么来看:就比如一个小孩出生,假如这个小孩是00后。他的父母是70后,80后。那么这个小孩是不是需要关注他的父母从出生到生下他这段时间里面他的父母做过什么?一般情况下,是不需要的。另外,也比如一个革命战士,比如一百年前,几十年前的革命战士。他们在他们的那个年代努力奋斗。但是他们看不看的到如今的中国是什么样子的呢?答案是也看不到。——每一个人,都只是需要看到自己从出生到死亡的这一段时间内的事情。其他时间段的时间,这些人不需要关心。而这些,其实就是隔离性。就是说我们不用获取信息总是获取最新的,也不能获取信息总是获取最旧的。我们只需要获取自己这个时间段内的就行。
所以,对于update和select谁先跑,应该是取决于谁先来的,谁先来的谁就先跑。虽然事务有执行中,事务有可能交叉,但是事务是一个原子整体。对于事务来说,谁先到来,谁就先执行。但是对于先到来的事务不一定先退出。可能先到的执行的很慢,后到来的执行的很快。那么后到来的先执行完,先到来的后执行完。而且可能在我们到来的期间,另一个事务把数据更新了。然后我们此时select应不应该看到最新的数据呢?——答案是不应该!!!因为我们要保证隔离性。
另外,隔离是运行中的事务,进行相互隔离。在事务运行中,"不会"出现互相干扰,此时就有了隔离性。根据影响程度的不同,就有了隔离级别。
隔离级别
- 读未提交:事务1所做的任何修改,不需提交事务2也能立即看到。
- 读提交:事务1所做的任何修改,只有当提交后事务2才能立即看到。
- 可重复读:事务1无论做任何修改以及提交。只有当事务2也结束后,再启事务才能看到。隔离级别较高,也是MySQL默认的隔离级别。
- 串行化:最高的隔离界别所有的CURD操作,必须按照到来的顺序一个一个执行,一个执行完才能下一个。
隔离试验示例
如何查,设置MySQL的隔离界别
要查看隔离界别,首先要区分什么是会话隔离界别,什么是全局隔离级别。会话隔离界别就是session.tx_isolation,也就是只影响这一次登录。而全局隔离级别global.tx_isolation也影响后续的登录。
select @@global.transaction_isolation; --查看全局隔离级别
select @@transaction_isolation; --查看会话(当前)全局隔离级别
select @@session.transaction_isolation; --查看会话隔离界别
然后设置隔离界别
set [session | global] transaction isolation level {read uncommitte | read committed | repeatable read | serializable}
上面的方括号里面的session和global二选一,设置系统还是会话。然后花括号里面的四个选择一个。读未提交,读提交,可重复读,串行化。
现在试验一下串行化
设置当前会话串行化:
set session transaction isolation level serializable;
但是新起会话不收到影响:
再设置回来:
set session transaction isolation level read uncommitted;
如果全局隔离级别和会话隔离级别不同,就会默认使用会话隔离级别。
读未提交
我们现在来读未提交。先启动两个事务:
然后右边查看一下表中的数据:
然后在左边插入数据张三:
之后再从右边查看,就能看到数据已经查看到了。
我们左边还没提交呢,右边就能看到。这就叫什么,这就叫做读未提交!一个事务在执行中,读到另一个事务的更新但是还没有commit的数据,这种情况叫做脏读。——这是一种不合理的现象。
rollback回滚也能看到数据又恢复了:
读提交
先将事务的隔离级别改为读提交:
两边启动事务:
先查看一下表中的数据:
然后我们插入数据张三:
然后右边查一下数据,会发现看不到表数据的更新:
但是我们左边查的话就能看到:
然后我们左边提交commit:
右边再查,就会看到数据修改了:
这种一旦提交就能看到的就叫做读提交。读提交好像解决了脏读的问题。但是又出现了另一个问题:就是我们左边在提交前和提交后,我们右边select查到的数据结果是不一样的。也就是说我们右边一直select,结果某一次select的时候,数据突然变了。——这种现象,就叫做不可重复读。
那么,不可重复读是问题吗?
——首先,我们提交了,就让另一个人看到,不应该吗?其实是应该的。但是不应该让另一个人在运行期间看到,应该是让另一个人结束后,再起事务的时候看到。
就比如我们的一个表,有员工ID,有员工名字,有员工薪资。如果路人A想要涨工资,就和老板去谈。谈妥了,老板就告诉路人B修改一下数据库中的路人A的薪资。这个时候呢,路人C正在查数据库中的人们的薪资,根据薪资来归类。所以呢,这个时候路人C通过筛选,先查工资(1000-2000)的人,里面有路人A。然后呢,路人B就对路人A的薪资进行修改,修改成了2500。然后路人C查的有点慢,他敲了半天,终于查出了(2000-3000)的工资的人。然后一看,里面也有路人A。那么这就出现了问题,这个路人A出现了两次。那么这个路人C对于路人A到底该怎么分类,就出现问题了。
所以,不可重复读有问题吗?答案是有问题。
可重复读
先设置可重复读:
启动我们的事务:
然后查看我们的数据:
然后我们进行插入,之后右边查看,会发现,右边没有数据更新:
然后我们左边直接提交了,右边也看不见:
这种即便commit,其他事务也看不到,就叫做可重复读。
然后重启事务,就能看到了:
对于可重复读,有的数据库因为insert的插入有些特殊,insert不能可重复读。所以就形成了幻读现象,也就是好像出现了幻觉一样。MySQL是解决了这个问题的。
串行化
设置隔离级别:
然后启动事务,先启动左边事务,再启动右边事务:
然后左侧查找,能将数据查出来:
然后右边我们也查,也能查出来:
然后我们右边删除一下数据张三,我们会发现,右边的事务卡在这里了:
然后我们左边的事务commit一下,右边的事务就启动了。
然后我们重启左边事务,又在右边删除数据田七。会发现右边不阻塞了。
上面这种现象就是串行化的现象,一开始左边事务先启动,默认他先执行。然后右边事务即便修改,也会阻塞在那里。然后我们左边事务提交了,再启动。它的顺序相当于在右边事务的后面了。右边事务就不会被阻塞了。
所以我们发现一个问题,就是如果我们的客户端里面是查的话,就直接执行了。但是如果是增删改操作,就会判断是不是该这个客户端执行,如果不是,就会将sql放到等待队列里。前面的事务结束了,这些sql才能一个一个的执行。
通过上面的例子我们就能看出来,隔离级别越严苛,安全性就越高。但是数据库的并发度就越低。所以就要在其中找到一个平衡点。根据实际需求,用户就可以使用不同的隔离方案。决定在用户,不在于数据库,所以MySQL提供了四种隔离级别!!!