一篇就够了系列:事务隔离级小结

事务是并发操作的副产品,就是描述并发造成的各种与预期不一致的几个场景。
如果想又好又快,就往下看喽

方便老手速读:

一、 什么是事务【事务的一些标签/特点】 (这段只是为了文章的完整性,老手可跳过)

ACID

度量事务的四个维度:

(1) 原子性(Atomicity):事务是数据库的逻辑工作单位,事务中包括的诸操作要么全做【全部成功】,要么全不做【全部失败】。
(2) 一致性(Consistency):事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态,一致性与原子性是密切相关的。
(3)隔离性(Isolation):用户并不会受同一时间段内其他用户操作的影响;看上去各个事务像是一前一后串行(Serially)发生的,哪怕当前有很多个并发事务。
(4)持续性/永久性(Durability):一个事务一旦提交,它对数据库中数据的改变就应该是永久性的,其他操作或故障不应该对其有任何影响。

对事务解读的其它视角(看看会不会被击中,能不能建立连接): (老手可跳过)

事务就是一件完整要做的事情。是数据库中各种数据项的一个执行单元。【原子性】
事务必须始终保持系统处于一致的状态,不管在任何给定的时间或并发的事务有多少。【一致性】
事务是恢复和并发控制的基本单位。【原子性】
事务在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序。这些操作要么都做,要么都不做,是一个不可分割的工作单位,即一个事务是一个原子操作。【原子性】

二、事务隔离级别

1. 隔离级别的由来及这个概念解决的问题

SQL的标准定义了4个隔离级别,设定一定的规则,限定事务内部的那些改变是可见的,那些是不可见的。低级别的事务一般会支持更高的并发处理,涉及更低的系统开销。

插播两个名词,预热一下。(老手可跳过)
两个概念:

  • 提交【COMMIT】:代表 操作全部完成。下一步,事务就正常结束了。
    补充描述:【完成了事务的所有操作,具体地说就是将事务中所有对数据库的更新写回到磁盘上的物理数据库中去,事务正常结束】。
  • 回滚【ROLLBACK】:代表 操作撤消。下一步,需要将执行期间的修改回退成操作前的样子。
    补充描述:【在事务运行的过程中发生了某种故障,事务不能继续进行,系统将事务中对数据库的所有以完成的操作全部撤消,滚回到事务开始的状态或设置的回滚点】。
2. 并发带来的问题(按约束递增排序)

(1)丢失更新【第一类】(Lost to modify):
现象:两个事务都同时更新一行数据,但是第二个事务却中途失败退出, 导致对数据的两个修改都失效了。
例如:
  张三的工资为5000,事务A中获取工资为5000,事务B获取工资为5000,汇入100,并提交数据库,工资变为5100,
  随后
  事务A发生异常,回滚了,恢复张三的工资为5000,这样就导致事务B的更新丢失了。【收到的100元钱没有了(人还在,钱没了)】。

第一类丢失更新是需要一定条件的。即,两个事务并发,一个回滚、一个提交成功,并不一定会导致丢失更新。
如果回滚逻辑严谨,执行相关逻辑的逆运算前再重新取下最新的值,那么数据的更新就不会丢失。
Tips:
目前主流的数据库(MySQL和Oracle)已经消灭了第一类丢失更新

(2)脏读(Dirty read):一个事务读取到了另外一个事务没有提交的数据。
脏读就是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个未提交的数据。
例如:
  张三的工资为5000,事务A中把他的工资改为8000,但事务A尚未提交。
  与此同时,
  事务B正在读取张三的工资,读取到张三的工资为8000。
  随后,
  事务A发生异常,而回滚了事务。张三的工资又回滚为5000。
  最后,
  事务B读取到的张三工资为8000的数据即为脏数据,事务B做了一次脏读。

(3)不可重复读(Unrepeatable read):在同一事务中,多次(大于1次)读取同一数据,得到的内容不同。
补充描述:【并发update场景。在一个事务内,会多次读同一数据,但在这个事务还没有结束时,另外一个事务也访问该同一数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的】。
这样就发生了在一个事务内两次读到的数据是不一样的,因此称为是不可重复读。
例如:
  在事务A中,读取到张三的工资为5000,操作没有完成,事务还没提交。
  与此同时,
  事务B把张三的工资改为8000,并提交了事务。
  随后,
  在事务A中,再次读取张三的工资,此时工资变为8000。在一个事务中前后两次读取的结果并不致,导致了不可重复读。
  
不可重复读的特例:第二类丢失更新。
有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交,而另一个也进行了修改提交。这就会造成第一次写操作失效。
例如:
在事务A中,读取到张三的存款为5000,操作没有完成,事务还没提交。
  与此同时,
  事务B,存储1000,把张三的存款改为6000,并提交了事务。
  随后,
  在事务A中,存储500,把张三的存款改为5500,并提交了事务,这样事务A的更新覆盖了事务B的更新。
  
(4) 幻读(Phantom/ˈfæntəm; fanˈtəm/ read):事务期间,多次(大于1次)查询到的记录数是不一样的。
补充描述:【并发insert或delete场景。例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象发生了幻觉一样】。
例如:
  目前工资为5000的员工有10人,事务A读取所有工资为5000的人数为10人。
  此时,
  事务B插入一条工资也为5000的记录。
  这是,事务A再次读取工资为5000的员工,记录为11人。多了一条记录。这种情况称为幻读。

Tips:

不可重复读的重点是修改,同样的条件,你读取过的数据,再次读取出来发现值不一样了
幻读的重点在于新增或者删除,同样的条件,第 1 次和第 2 次读出来的记录数不一样

3、事务隔离级别,解决什么并发问题,以及存在什么并发问题
(1)READ_UNCOMMITTED
  这是事务最低的隔离级别,它充许另外一个事务可以看到这个事务未提交的数据。
  解决第一类丢失更新的问题,但是会出现脏读、不可重复读、第二类丢失更新的问题,幻读 。
(2)READ_COMMITTED
  保证一个事务修改的数据提交后才能被另外一个事务读取,即另外一个事务不能读取该事务未提交的数据。
  解决第一类丢失更新和脏读的问题,但会出现不可重复读、第二类丢失更新的问题,幻读问题
(3)REPEATABLE_READ
  保证一个事务相同条件下前后两次获取的数据是一致的。

解决第一类丢失更新,脏读、不可重复读、第二类丢失更新的问题,但会出幻读。
(4)SERIALIZABLE
  终结并发,全部串行。
  解决所有问题

Tips:

(1)MySQL数据库中,支持上面四种隔离级别,默认的为Repeatable read (可重复读);
(2)Oracle数据库中,只支持Serializable (串行化)级别和Read committed (读已提交)这两种级别,其中默认的为Read committed级别。
(3)PostgreSQL不支持Read Uncommitted 隔离级别,不允许脏读(Dirty read)
PostgreSQL实现的Repeatable read,也不会产生幻读(Phantom read)
PostgreSQL默认的事务隔离级别:Read committed。

https://mp.weixin.qq.com/s/_p6VTreremicz62rYh9J3g

原文地址:https://www.cnblogs.com/softidea/p/10224211.html