暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

数据库事务隔离级别

ITSK 2021-05-17
630
没有什么事情比让自己变优秀更重要,按自己的节奏,有计划努力就好啦嘻嘻,fighting~~




最近工作中遇到业务多线程并发访问数据出现数据查询不到的问题,所以回顾了一下数据库事务的隔离级别的基本知识,简单做了总结如下文所述。
什么是数据库事务

事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果将使数据库从一种一致性状态变迁到另一种一致性状态。事务是逻辑上的一组操作,要么全部执行,要么全部不执行。

事务的四大特性(ACID)
  • 原子性
    事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用。
  • 一致性
    执行事务前后,数据保持一致,多个事务对同一数据读取的结果是相同的。
  • 隔离性
    并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务数据库之间是独立的。
  • 持久性
    一个事务被提交之后,它对数据库中数据的改变是持久的,即使数据库发生故障,也不应该对其有任何影响。

SQL标准定义了四个隔离级别

  • READ UNCOMMITTED(读取未提交)

    最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、不可重复读、幻读。

  • READ COMMITTED(读取已提交)

    允许读取并发事务已提交的数据,可以阻止脏读,但是不可重复读、幻读仍有可能发生。

  • REPEATABLE READ(可重复读)

    对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。

  • SERIALIZABLE(串行化)

    最高的隔离级别,完全服从ACID的隔离级别,所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说该级别可以防止脏读、不可重复读、幻读。


什么是脏读、不可重复读、幻读
  • 脏读

    某个事务更新了一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个事务进行了ROLLBACK操作,则后一个事务读取的数据就会是不正确的。

  • 不可重复读

    在一个事务的两次查询之中数据不一致,一个未提交事务读取了另一个事务更新已提交的数据。

  • 幻读

    在一个事务的两次查询中数据的记录数不一致,例如有一个事务查询了几行数据,而另一个事务在此时插入了几行数据并进行了提交,先前的事务在接下来的查询中就会发现有几行数据是先前查询中所没有的。



四种隔离级别场景演示
  • 读未提交


①设置会话一的事务隔离级别为:读未提交,然后读取id为2的账户余额为8000:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;-- 设置当前会话数据库隔离级别为【读未提交】
SELECT * FROM t_account WHERE id = 2;


②设置会话一的事务隔离级别为:读未提交,开启一个事务,修改id为2的账户的余额为6000,未进行事务提交,然后在当前事务查询结果为修改后的值:
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;-- 设置当前会话数据库隔离级别为【读未提交】
START TRANSACTION; -- 开启事务
UPDATE t_account ta SET ta.amount = "6000" WHERE id = 2; -- 修改id为2的账户余额为6000
SELECT * FROM t_account WHERE id = 2; -- 当前事务为提交,在当前事务进行查询

③此时会话一如果再读取id为2的账户余额结果如下:

④会话二中事务如果进行回滚操作
⑤此时会话一再进行读取即读取到了会话二中事务未提交的数据,导致前后读取到的id为2的账户余额不一致;
  • 读已提交

①设置会话一、会话二的隔离级别为读已提交,然后进行上述步骤:会话二中事务进行修改id为2的账户余额但未提交,在当前事务可以查询到修改后的值
②在会话一中查询结果如下:没有查询到会话二中修改未提交的数据,只要在会话二将事务提交以后才可以查询到修改以后的数据,可以解决脏读的问题
会话二事务进行提交:
会话一中开启事务查询如下:
事务一只有在事务二提交以后才能读取到事务二修改的数据,此时确实解决了脏读的问题(事务一读取到事务二中未提交的数据),但是还有一个新的问题如下;
③接着上述步骤,会话二中又开启一个事务:修改id为2的账户余额为5000并进行提交如下:
④此时会话一种还是在当前事务中进行读取发现id为2的账户余额又变为了5000,出现了不可重复读的问题:即在同一个事务中前后两次读取到的id为2的账户的余额不一致:
  • 可重复读

①设置会话一的事务隔离级别为:可重复读,然后开启事务一:读取id为2的账户余额为5000,此时未进行事务提交:
设置会话一的事务隔离级别为:可重复读,然后开启事务二:修改id为2的账户余额为4000,然后提交事务如下:
③此时事务一再进行读取如下:该事务未提交时:始终读取到的是事务二未进行修改的数据,只有当前事务提交以后才会读取到事务二已修改数据:即解决了可重复读的问题;
④事务一提交后读取结果如下:
解决了不可重复读的问题,但是又存在如下幻读的问题:
⑤会话一中事务一执行如下:
⑥此时会话二开启了事务:插入一条数据并进行事务提交如下:
⑦此时会话一中事务在进行插入id为4的数据的时候报错如下:
但是此时在事务一中只查询到三条记录如下:并没有查询到事务二新插入的id为4的数据,但是在插入的时候还报错,主键冲突不能插入,所以存在幻读的问题;
事务一进行事务提交以后才可以查询到事务二新插入的数据如下:
  • 可串行化

①设置会话一、会话二的事务隔离级别为SERIALIZABLE,然后在会话一种开启一个事务未提交,在会话二中开启一个事务,插入id为5的数据然后提交,在事务二中查询结果如下:
②此时事务一种未进行事务提交也能查询到事务二新增的数据,即不会存在幻读的问题:

至此数据库隔离级别的基本知识就总结完毕了,想要一起交流的小伙伴可以留言哦~~

长按二维码关注我们

ITSK

博客|yajing8


我知道你在看

文章转载自ITSK,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论