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

咱们知道,事务是一种保障数据原子性、一致性、隔离性和持久性(ACID)的重要机制。
可是用了事务就是安全的吗?用了事务就能100%正确的读写数据了吗?
若是你没用搞懂数据库的隔离机制就盲目使用事务,最终会致使没法确保数据一致性又或者没法对应高并发场景下的数据读写等等问题。mysql

脏读、不可重复读、幻读是如何发生的

让咱们来看一些例子,在隔离等级为Read uncommitted(咱们后面再说)的状况下执行如下事务操做。 由于没有选择适当的隔离方法,这些操做最终都会发生期待结果和实际结果不同的状况。面试

脏读

T1读取了T2未提交的数据。

不可重复读

T1虽然读取到了T2提交的数据,但却不是T1事务开启时的数据。

幻读

T1虽然读取到了T2提交的数据,但这个数据是T1事务开启时不存在的数据。

若是这是一个我的网站,并且用户数很少,每一个用户的请求都在不一样时间发生,那么上述的状况基本上是不会发生的。 可是若是这是一个高并发场景,并且读写的都是热数据,那么读错或者写错数据的可能性就会大大增长。sql

如何防止脏读、不可重复读、幻读

有人可能要问了,那么这些状况要怎么避免呢?数据库

主流的关系型数据库都提供了隔离等级,以MySQL为例:安全

MySQL的隔离等级

MySQL提供了4种隔离等级,等级越高越能保持读写的一致性。 但为了保持一致性也付出了必定的成本。好比说串行化(Serializable),它要求事务序列化执行,好比一个事务在写的时候其余事务不能读。在高并发场景下对系统的负载是比较高的。 并发

隔离等级:READ COMMITTED

隔离等级:REPEATABLE READ

不一样的隔离等级的实现方法

重点来了,讲了这么多概念和现象,若是面试官问
为何READ COMMITTED能够避免脏读?
为何REPEATABLE READ能够避免不可重复读?
你能答上来吗?高并发

多版本并发控制(Multiversion Concurrency Control)

在MySQL的表中除了用户定义的列之外还会有隐藏列。post

列名 长度(字节) 做用
DB_TRX_ID 6 插入或更新行的最后一个事务的事务标识符。(删除视为更新,将其标记为已删除)
DB_ROLL_PTR 7 写入回滚段的撤消日志记录(若行已更新,则撤消日志记录包含在更新行以前重建行内容所需的信息)
DB_ROW_ID 6 行标识(隐藏单调自增id)

以READ COMMITTED为例,

每当事务对数据进行修改的时候都会将旧的数据会被备份到undo日志中, 而后更新数据库, 再将新数据的DB_ROLL_PTR指向undo日志中被复制的那一条数据。网站

每当同一条数据被不一样事务修改时会重复上面的操做, 最终会造成一个经过DB_ROLL_PTR不断指向旧数据的版本链。3d

而T1之因此可以读到Empty是由于它读取的不是数据库的最新内容,而是版本链中和TRX_ID == 1的最新状态的数据。

下面这篇文章对MVCC作了详细的介绍,这里就不作赘述了。
推荐阅读:MySQL事务隔离级别和MVCC

记忆方法

脏读

“脏”是相对于“干净”而言的。“干净”的数据就是已经提交了的数据,而“脏”数据是提交以前的数据。读到了提交以前的数据就叫脏读。

不可重复读

重复说的是每一次读取某一条数据结果都是同样的。 不可重复读就是说在一次事务中,两次读取的结果不一样,说明在两次读取的过程当中有别的事务修改了这条记录。

幻读

幻即幻觉,就是读到了本来不存在的数据。 那么为何会读到不存在的数据呢,由于在事务过程当中别的事务插入了一条记录致使读到了本来不存在的数据。

参考文章

脏读、幻读与不可重复读
MySQL事务隔离级别和MVCC
MySQL中InnoDB的多版本并发控制(MVCC)

相关文章
相关标签/搜索