SQL 多个事务并发时可能遇到的问题

Reference

事务并发的可能问题与其解决方案
脏读、幻读、不可重复读和丢失更新
数据库并发事务存在的问题(脏读、不可重复读、幻读等)
对于脏读,不可重复读,幻读的一点理解,看懂红字很关键
MySQL 四种事务隔离级的说明
15.7.2.1 Transaction Isolation Levels
MySQL Innodb 事务隔离级别
【漫画】如何给新来的师妹解释什么是数据库的脏读、不可重复读和幻读

多个事务并发时可能遇到的问题

  1. Lost Update 更新丢失 (两类)
    a. 第一类更新丢失,回滚覆盖
    b. 第二类更新丢失,提交覆盖
  2. Read Phenomena 读现象 (三种)
    a. Dirty Reads 脏读
    b. Non-Repeatable Reads 不可重复读
    c. Phantom Reads 幻读

更新丢失

第一类更新丢失,回滚覆盖

定义

撤消一个事务时,在该事务内的写操作要回滚,把其它已提交的事务写入的数据覆盖了。

解释
实例
时间 取款事务 A 转账事务 B
T1 开始事务
T2 开始事务
T3 读余额为 1000
T4 取出 100, 余额改为 900
T5 读余额为 1000
T6 汇入 100, 余额改为 1100
T7 提交事务, 余额定位 1100
T8 撤销事务, 余额改回 1000 -
T9 最终余额为 1000,更新丢失 -

写操作没加 “持续 - X 锁”,没能阻止事务 B 写,发生了回滚覆盖。

第二类更新丢失,提交覆盖

定义

提交一个事务时,写操作依赖于事务内读到的数据,读发生在其他事务提交前,写发生在其他事务提交后,把其他已提交的事务写入的数据覆盖了。这是不可重复读的特例。

解释
实例
时间 取款事务 A 转账事务 B
T1 开始事务
T2 开始事务
T3 读余额为 1000
T4 读余额为 1000
T5 取出 100, 余额改为 900
T6 提交事务, 余额定位 900
T7 汇入 100,余额改为 1100 -
T8 提交事务,余额定为 1100 -
T9 最终余额为 1100,更新丢失 -

写操作加了 “持续 - X 锁”, 读操作加了 “临时 - S 锁”,没能阻止事务 B 写,发生了提交覆盖。

Read Phenomena 读现象

Dirty Reads 脏读

定义

一个事务读到了另一个未提交的事务写的数据。

针对未提交数据

解释

当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交(commit)到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据。因为这个数据是还没有提交的数据,那么另外一个事务读到的这个数据是脏数据,依据脏数据所做的操作可能是不正确的。

实例
时间 取款事务 A 转账事务 B
T1 开始事务
T2 开始事务
T3 读余额为 1000 查询账户余额为 1000
T4 取出 500, 余额改为 500
T5 读余额为 500 (脏读)
T6 撤销事务,余额恢复为 1000
T7 汇入 100,余额改为 600 -
T8 提交事务 -
T9 最终余额为 600,丢失 400 -
解决

修改时加排他锁,直到事务提交后才释放,读取时加共享锁,读取完释放事务 A 读取数据时加上共享锁后(这样在事务 A 读取数据的过程中,其他事务就不会修改该数据),不允许任何事务操作该数据,只能读取,之后 A 如果有更新操作,那么会转换为排他锁,其他事务更无权参与进来读写,这样就防止了脏读问题。但是当事务 A 读取数据过程中,有可能其他事务也读取了该数据,读取完毕后共享锁释放,此时事务 A 修改数据,修改完毕提交事务,其他事务再次读取数据时候发现数据不一致,就会出现不可重复读问题,所以这样不能够避免不可重复读问题。

Non-Repeatable Reads 不可重复读

定义

一个事务中两次读同一行数据,可是这两次读到的数据不一样。

针对其他提交前后,读取数据本身的对比
不可重复读重点在修改

解释

在数据库访问中,一个事务范围内两个相同的查询却返回了不同数据。这是由于查询时系统中其他事务修改的提交而引起的。比如事务T1读取某一数据,事务T2读取并修改了该数据,T1为了对读取值进行检验而再次读取该数据,便得到了不同的结果。

实例
时间 取款事务 A 转账事务 B
T1 开始事务
T2 读取余额为 1000
T3 开始事务
T4 取出 500, 余额改为 500
T5 提交事务
T6 再次读取余额为 500 (不可重复读取)
解决

读取数据时加共享锁,写数据时加排他锁,都是事务提交才释放锁。读取时候不允许其他事物修改该数据,不管数据在事务过程中读取多少次,数据都是一致的,避免了不可重复读问题。

Phantom Reads 幻读

定义

同一个事务内多次查询返回的结果集不一样(比如增加了或者减少了行记录)。

针对其他提交前后,读取数据条数的对比
幻读是不可重复读的一种特殊场景。
幻读重点在新增或删除

解释

比如同一个事务A内第一次查询时候有n条记录,但是第二次同等条件下查询却又n+1条记录,这就好像产生了幻觉。
没有范围锁

实例
时间 取款事务 A 转账事务 B
T1 开始事务
T2 查询交易记录次数
T3 开始事务
T4 新增交易记录
T5 提交事务
T6 再次查询交易记录次数 (幻读) -
解决

实行 Serializable 隔离模式: 采用范围锁 RangeS - RangeS-S Mode,锁定检索范围为只读,这样就避免了幻读问题。


数据库通过锁机制解决并发访问的问题。根据锁定对象不同:分为行级锁和表级锁;根据并发事务锁定的关系上看:分为共享锁定和独占锁定,共享锁定会防止独占锁定但允许其他的共享锁定。而独占锁定既防止共享锁定也防止其他独占锁定。为了更改数据,数据库必须在进行更改的行上施加行独占锁定,insert、update、delete和selsct for update语句都会隐式采用必要的行锁定。

但是直接使用锁机制管理是很复杂的,基于锁机制,数据库给用户提供了不同的事务隔离级别,只要设置了事务隔离级别,数据库就会分析事务中的sql语句然后自动选择合适的锁。

事务的隔离级别和数据库并发性是成反比的,隔离级别越高,并发性越低。

你可能感兴趣的:(SQL 多个事务并发时可能遇到的问题)