Mysql专栏(三)** Mysql隔离级别与MVCC

本篇文章手动加星,很重要哦。
Mysql成为我们现在系统选型不可或缺的一部分,很大一部分功劳归功于其事务特性,本篇文章深入底层讲解其MVCC相关知识。

Mysql事务基本概念

事务的四个特性ACID不再赘诉

  • 原子性:undo log + 隔离性 + 悲观锁,共同保证了一个事务中的操作要么全成功,要么全失败,而其他事务不会看到、修改另一个事务的中间状态。
  • 一致性:(个人理解,不一定正确)多个事务会按照开发者期待的顺序执行并得到预期结果。
  • 隔离性:MVCC与Mysql隔离级别来保证不同事务的数据隔离。
  • 持久性:Mysql使用redo log机制保障数据不会发生丢失。

MVCC(多版本并发控制)

可以看到事务特性基本上都涉及到了MVCC,其可以说是Mysql的一个最重要的组件,其主要用于协调多个事务之间的并发操作。

当数据库支持事务时,在Mysql中的每张表中的每一行记录会额外存储两个字段DB_TRX_ID和DB_ROLL_PTR,DB_TRX_ID为该行记录插入或更新的最后一个事务的事务,而DB_ROLL_PTR指向了undo log日志的记录指针。
Mysql专栏(三)** Mysql隔离级别与MVCC_第1张图片
当对id=1的记录修改了多次值以后,其undo log指针会形成一条链表,如下所示(text从a -> b -> c)
Mysql专栏(三)** Mysql隔离级别与MVCC_第2张图片
该种数据结构 + 读视图 构成了整个MVCC,下面介绍读视图的概念。

读视图

为了能够使得不同事务读取到不同的数据(隔离性),Innodb在读取数据的时候会为每个事务生成一个读视图,读视图包含以下部分,

  • trx_list:当前活跃事务列表
  • up_limit_id:活跃事务列表中最小的事务id
  • low_limit_id:系统最大的事务id值

在读视图中会维护生成读视图那一刻的,当前数据库的trx_list、up_limit_id、low_limit_id。然后根据数据行的undo log链中根据可见性算法进行匹配当前事务可以读到哪些行。
可见性算法:

  1. undo log记录的DB_TRX_ID小于当前活跃最小事务up_limit_id,说明该行记录在生成读视图前已经被提交,该记录可见,继续在undo log链上继续判断
  2. undo log记录的DB_TRX_ID大于当前活跃最小事务up_limit_id,则查看该事务是不是位于活跃事务列表中,如果位于说明操作该数据行的事务还没有提交,本事务无法读取。
  3. undo log记录的DB_TRX_ID大于读视图时的最大事务low_limit_id,则该记录是生成读视图后才产生的记录,对当前事务不可见。

隔离级别

重要概念涉及:

  • 当前读:读取最新的数据,可能是内存buffer中,也可能直接是磁盘上数据。update、delete等语句都是当前读
  • 快照读:按照隔离级别的约束规则,从MVCC生成的读视图中读取数据,而不是读取最新的数据。

innnodb中,隔离级别有以下四种

  1. 未提交读 Read uncommitted
  2. 提交读(或者不可重复读) Read committed
  3. 可重复读 Repeatable read
  4. 串行化 Serializable

未提交读相当于多个事务之间不存在隔离性,彼此都能看到其他人做出的更改,哪怕其他的事务并没有进行提交。该种隔离级别会造成严重后果,如果别的事务回滚了,当前事务读取到的数据就是脏读,所以基本上没有人会使用。

提交读RC实际上用的挺多的,其原理为在一个事务的每次快照读的时候,都会生成一个读视图,可能会出现在同一个事务内,多次读取到的数据是不同的,所以也称为不可重复读

可重复读是innnodb默认的隔离级别(不过讲道理,默认级别改成RC,一个事务内能读到其他事务已经提交的数据我觉得没有什么问题)。在一个事务内,只有第一次快照读的时候才会生成读视图,其后的快照读会重复使用该视图,从而解决了不可重复读的问题。

串行化这个最简单,事务只能排队执行,不存在并行情况,非常影响性能,所以也没什么人使用。


讲到这里,有人可能会想了,可重复读下不是有幻读的问题吗,听起来好像是个很严重的问题。
其实个人来讲,我觉得幻读并不是问题,只要你能把隔离级别和MVCC能串起来,和别人讲清楚,我觉得Mysql这里已经是个及格分了。

==============
下面来讲什么是幻读?
Mysql专栏(三)** Mysql隔离级别与MVCC_第3张图片
官方文档中的例子演示的是幻读的最典型场景,在一个事务中,当前读和快照读混用。上面已经提到了,当前读和快照读所读取的数据其实是不同的,一个是快照数据,一个是最新数据,这两个数据本来就可能是不同的,我个人不知道这种场景为什么就变成了幻读这个名词。而且这不会对事务ACID产生问题,顶多只是开发者会疑惑一下,为什么我前面查的数据是这样,更新的结果又是这样。。

你可能感兴趣的:(Mysql专栏,mysql,数据库,database)