数据库事务的隔离级别
数据库事务( transaction)是访问并可能操作各种数据项的一个数据库操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。 --百度百科
众所周知,事务有ACID特性(原子性、一致性、隔离性、持久性),其中隔离性又包括四个级别:
- READ_UNCOMMITTED:读未提交, 这种隔离级别指的是A事务可以读取到B事务还没有提交的数据,显然这种隔离级别是不安全的,它容易引发不可重复读、脏读和幻读。
- READ_COMMITTED:读已提交,这种隔离级别指的是A事务只能读取到B事务已经提交的数据,这种隔离级别比上面一种安全一些,但是只能防止脏读。
- REPEATABLE_READ:可重复读,虽然名字叫可重复读,但是它的意思是:重复读同一条数据的情况下,能保证读的结果是一致的。不少人被这个名字误解,它的原理大致是A事务在开始后会将数据库中的数据假冻结,也就是说无论B事务对数据作什么样的改变,A事务读到的数据都是事务开始时的样子,这样就做到了无论读多少次数据都是一致的。但是这种情况下还是会有幻读的情况出现。
- SERIALIZABLE:串行化,这种隔离级别也是最高的,所有的事务都必须像单线程一样,一个个执行。它可以防止幻读。
四种隔离级别中,并发编程下:1的性能最高,但是最不安全,4的性能最低,但是最安全,mysql中的默认隔离级别是REPEATABLE_READ。
查看mysql8.0的事务隔离级别
/*查看当前会话的事务隔离级别*/ select @@transaction_isolation; /*查看mysql的事务隔离级别*/ select @@global.transacton_isolation;
修改mysql8.0中的事务隔离级别
/*设置当前会话的隔离级别 */ /*设置read uncommitted级别*/ set session transaction isolation level read uncommitted; /*设置read committed级别*/ set session transaction isolation level read committed; /*设置repeatable read级别*/ set session transaction isolation level repeatable read; /*设置serializable级别*/ set session transaction isolation level serializable; /*设置mysql全局的隔离级别 */ /*设置read uncommitted级别*/ set global transaction isolation level read uncommitted; /*设置read committed级别*/ set global transaction isolation level read committed; /*设置repeatable read级别*/ set global transaction isolation level repeatable read; /*设置serializable级别*/ set global transaction isolation level serializable;
---------------------------------------------------------------------------------下面是实例演示:-----------------------------------------------------------------------------------------------------------
下面将用实例来演示四种隔离级别以及可能出现的问题:
首先先新增一张Student表, 表中有id,name,score字段,并填入测试字段
1.READ_UNCOMMITTED
然后我们打开两个会话,并将两个会话的事务隔离级别都设置为read_uncommitted
我们在left会话中先开启一个事务,然后查询s1的分数,此时可以看到分数是10
然后在right会话中再开启一个事务,然后把s1的分数改为99,但是先不要提交,也不要回滚
然后我们再去left会话中单独执行查询s1的分数语句,可以看到明明事务还没有提交, 却可以看到被修改成了99。这就是脏读
2.READ_COMMITTED
还是打开两个窗口模拟两个事务。 然后将两个窗口的事务隔离级别都修改为read_committed,因为修改方式和上面一样就不放图了。
然后还是在left中查询s1的成绩
然后再次在right中修改为99
再次执行在left中执行的话,可以看到还是10,因为我们改变了事务的隔离级别,right没有提交的修改我们在left中看不到,这种隔离级别就防止了脏读的出现
然后我们执行right的commit
再次在left中查询s1,可以看到成绩变为了99。因为right已经提交了事务,所以可以在left中看到,但是这又出现了left事务中同一条语句查询两次而得到的查询结果不一致的问题,这就是数据的不可重复读问题。
其实我觉得这个名字很容易误导很多人, 要是翻译成 "读取结果不一致" 的话可以更贴近事实
3.REPEATABLE_READ
可以从这个隔离级别的字面意思上理解“可重复读”,这个可重复读可以解决就可以解决不可重复读的问题。
我们还是先将left和right的隔离级别都设置为repeatable_read
和之前一样的,还是先在left中开启事务,查询s1
然后在right中开启事务,修改为100,最后直接提交
然后我们返回到left中查看,可以看到虽然right的事务提交了,但是left中的结果依然是99。这就是repeatable_read这个隔离级别解决了不可重复读的问题(其实换一种更直接的说法就是:repeatable_read这个隔离级别解决了多次读取数据不一致的问题,在一个事务当中,多次读取同一条数据,无论如何都是相同的)
但是这种隔离级别还是会有幻读的情况出现,比如说:先在left中开启事务, 查看所有的学习成绩
此时可以看到一共有s1和s2两条数据,然后我们去right中将s2删掉
然后再回到left中查看,还是两条,没有任何问题,因为repeatable_read会将当前事务中的数据进行假冻结
然后这时我们将s2的成绩修改成100,这时可以看到这个update语句的影响行数为0,这时我们再去重新执行select语句可以发现明明是有s2的,但是这条更新语句就是没有效果,就像我们看到的s2是幻觉一样,这就是幻读。
4.SERIALIZABLE
从字面意思上理解的话是序列化、串行化。 但是这里指的其实是所有的事务必须一个个的按顺序执行。这种隔离级别可以有效的防止以上任意一种错误出现
还是先将left和right的隔离级别都改为serializable
我们再重现刚刚的场景:先在left开启一个事务,然后查询student
然后在right中删除s2,可以看到right就一直是正在处理
如果一直等待的话这就会因为超时而无法执行,因为这个隔离级别的所有事务都必须像单线程一样一个个顺序执行,没有办法同时处理,只要left不提交,right就永远没有办法执行。所以right没有办法将s2删除, 自然就不会有幻读的情况出现了。不过正如我们看到的那样,因为隔离级别太高了,导致事务只能按顺序执行,这种的性能是非常差的。所以mysql退而求其次选择了可重复读(也就是多次读取时结果一致)这种隔离策略
最后总结一下:
① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。
② Repeatable read (可重复读):可避免脏读、不可重复读的发生。
③ Read committed (读已提交):可避免脏读的发生。
④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步