【学习记录】数据库隔离级别

数据库隔离可以在不同程度上减少丢失更新,隔离级别定义为4种:脏读、读/写提交、可重复读、序列化

在解释四种隔离之前插个知识点:第一类丢失更新

时间 事务一 事务二 备注
T1 余额1000 卡里总余额为1000
T2 余额1000
T3 淘宝花了200
T4 吃饭用了100
T5 提交事务,余额900
T6 不想买了,事务回滚到T2时刻,余额1000

整个过程中事务一用了100,但是在最后面 T6 回滚之后居然还是初始1000,这跟现实不符,就是第一类丢失更新(原因:数据库事务的隔离性,一个事务无法探知其他事务的操作)。

1、脏读(dirty read)

脏读是四种隔离级别中最低级的,其含义就是允许一个事务去读取另一个事务中还未提交的数据,举例说明(空白表示没有操作):

时间 事务一 事务二 备注
T1 查询余额100元 卡里只有100元
T2 查询余额100元
T3 淘宝买东西用了20,余额80
T4 淘宝买东西用了30,余额50 读取到事务二还未提交的数据,未提交的余额为80
T5 提交事务 这时卡里余额为50
T6 突然不想买,回滚事务 这时余额还为50

在 T3 时刻,事务二买东西了,所以事务一在 T4 时刻去买东西时,因为用了脏读,会去读事务二在 T3 时刻的数据(余额显示为80),所以事务一在 T5 提交事务之后,卡里余额就只剩50。事务二在 T6 时刻突然不想买,回滚事务后,由于数据库克服了以前的第一类丢失更新,余额还是50,这跟现实不符合,这种就是脏读。

2、读/写提交(read commit)

这是数据库第二个隔离级别,其含义就是一个事务只能去读取另一个事务中已经提交的数据(注意:已经提交,就是提交事务成功后的),下面举例说明:

时间 事务一 事务二 备注
T1 查询余额100元 卡里只有100元
T2 查询余额100元
T3 淘宝买东西用了20,余额80
T4 淘宝买东西用了30,余额70 事务二还未提交,所以余额为70
T5 提交事务 这时卡里余额为70
T6 突然不想买,回滚事务 这时余额还为70

T3 时刻因为用了读/写提交,事务二还未提交事务,所以事务一是无法知道事务二的数据的,所以余额显示为70,T6 时刻事务二回滚后显示的是正常数据。它克服了第一个脏读可能导致的数据错误。但是还是有可能出现错误,比如下面这个例子:

时间 事务一 事务二 备注
T1 查询余额100元 卡里只有100元
T2 查询余额100元
T3 淘宝买东西用了40,余额60
T4 淘宝买东西用了30,余额70 事务二还未提交,所以余额为70
T5 继续买50,余额10 事务一还未提交,所以余额为10
T6 提交事务 事务二提交,这时余额为10
T7 提交事务,发现余额只剩10,不能买单 事务一读取事务二已提交的数据,发现余额不足

这里的出现问题是什么呢?对于事务一来说,它并不知道事务二做了什么,但是钱确实只剩10了,对事务一来说余额是不能重复去读的,而是会变化,这种叫做:不可重复读。简单来说就是我没做什么,可是我卡里莫名其妙就少了90,导致我无法买单。

3、可重复读(repeatable read)

数据库为了克服不可重复读提出的新的隔离级别。这个针对的是数据库同一条记录,它会使同一条数据库记录的读/写按照序列化来操作,避免了同一条数据的不一致性。然而数据库有可能需要同时对多条记录进行读/写,这时候也会出现问题,下面举例:

时间 事务一 事务二 备注
T1 查询消费记录10条,准备打印 这是初始状态
T2 消费了一笔
T3 提交事务
T4 打印出来发现11条记录 最终打印结果

在 T4 时刻,事务二发现打印出来了11条,会认为这条应该是不存在的、多余。事务二为什么会质疑那多出的一条记录呢?因为事务二并不知道事务一进行了消费(因为这不是同一条记录,可重复读只针对同一条记录),事务二就会以为这是不存在的。这种叫: 幻读

4、序列化(serializable)

为了解决幻读问题,提出了系列化隔离级别,其含义是让SQL按照顺序读/写的方式操作,能够消除事务并发时产生的数据不一致。

5、总结:

并发事务可能会出现的问题:

  • 脏读(Dirty read)
  • 丢失修改(Lost to modify)
  • 不可重复读(Unrepeatableread)
  • 幻读(Phantom read)
隔离级别 脏读 不可重复读 幻读
脏读
读/写提交 ×
可重复读 × ×
序列化 × × ×

√:会出现这个问题;×:不会出现

6、 注意:
  • 数据库隔离级别是按照SQL标准规范,不是Spring 或者Java的规范,Spring和Java只是按照SQL的规范定义而已;
  • 不可重复读的重点是修改,比如多次读取一条记录发现其中某些列的值被修改;幻读的重点在于新增或者删除,比如多次读取一条记录发现记录增多或减少了;
  • 从脏读到序列化,级别越高,越会压制并发,隔离级别需要根据并发大小和性能决定;
7、关于MySQL:
  • MySQL InnoDB 存储引擎的默认支持的隔离级别是:可重复读;
    原因在于:InnoDB 存储引擎在可重复读事务隔离级别下使用的是Next-Key Lock 锁算法,可以避免幻读的产生,以完全保证事务的隔离性要求。InnoDB 存储引擎在分布式事务的情况下一般会用到序列化隔离级别。
  • 因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是:读/写提交;

来源注明:
(1)杨开振、周吉文等的《JavaEE互联网轻量级框架整合开发》
(2)JavaGuide面试突击

猜你喜欢

转载自blog.csdn.net/qq_42908549/article/details/105461640
今日推荐