INNODB MVCC

Posted 程序媛爱coding

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了INNODB MVCC相关的知识,希望对你有一定的参考价值。

在讲解InnoDB的MVCC机制之前,我们应该了解mysql所支持的事务,以及各个事务级别的区别和每一个事务级别所存在的问题。

1. 事务

事务必须保证ACID,而ACID表示原子性、一致性、隔离性和持久性

1.1 事务的隔离级别

事务可以通过start transaction语句开始一个事务,然后要么使用commit提交事务将所修改的数据持久保存,要么使用rollback撤销所有修改

1.1.2 READ UNCOMMITTED (未提交读 RU)

在READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读。

1.1.3 READ COMMITTED (提交读 RC)

大多数的数据库系统的默认隔离级别都是READ COMMITTED(MySQL 不是)。READ COMMITTED满足前面提到的隔离级别的简单定义:一个事务开始时,只能“看见” 已提交的事务所做的修改。换句话说,一个事务从开始知道提交之前,所做的任何修改对其他事务都是不可见的。这个级别也叫不可重复读,因为在同一事务内执行两次相同的查询,可能会得到不一样的结果。

例子: 当事务的隔离级别在RC级别的时候,事务A和事务B同时对数据D操作,当事务A开始的时候,读取的数据D保存下来了,这是事务B也在修改数据D,并且先于事务A提交。这是事务A再读数据D的时候,就会出现前后不一致情况,这就是所谓的不可重复读。

1.1.4 REPEATABLE READ (可重复读 RR)

这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读 (Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制解决了该问题。

例子:mysql的默认事务隔离级别是RR级别的,同样是上述例子,当时不同的是当事务A和事务B开始的时候,都保存一份自己的快照,每一份快照中都有数据D的值,所以这样在同一事务中,无论重读读多少次都是正确的。

例子:在RR级别中,可能出现幻读。同样是上述例子,事务A和事务B同时查询数据D,事务A发现数据D为空,就想插入数据,但是这是事务B已经插入了数据D并且已经提交。这时事务A的提交就会出错。这是因为事务A的写操作是当前读操作。

1.1.5 SERIALIZABLE (可串行化 S)

这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。

隔离级别

脏读可能性

不可重复可能性

幻读可能性

加锁读

READ UNCOMMITTED Yes Yes Yes No
READ COMMITTED No Yes Yes No
REPEATABLE READ No No Yes No
SERIALIZABLE No No No Yes

2. MVCC机制

InnoDB的一致性的非锁定读就是通过在MVCC实现的,Mysql的大多数事务型存储引擎实现的都不是简单的行级锁。基于提升并发性能的考虑,它们一般都同时实现了多版本并发控制(MVCC)。MVCC的实现,是通过保存数据在某一个时间点的快照来实现的。因此每一个事务无论执行多长时间看到的数据,都是一样的。所以MVCC实现可重复读。

  • 快照读:select语句默认,不加锁,MVCC实现可重复读,使用的是MVCC机制读取undo中的已经提交的数据。所以它的读取是非阻塞的

  • 当前读:select语句加S锁或X锁;所有的修改操作加X锁,在select for update 的时候,才是当地前读。

RR隔离级别下的快照读,不是以begin开始的时间点作为snapshot建立时间点,而是以第一条select语句的时间点作为snapshot建立的时间点。

2.1. MVCC依赖数据

行记录隐藏字段

  • db_row_id,行ID,用来生成默认聚簇索引(聚簇索引,保存的数据在物理磁盘中按顺序保存,这样相关数据保存在一起,提高查询速度)

  • db_trx_id,事务ID,新开始一个事务时生成,实例内全局唯一

  • db_roll_ptr,undo log指针,指向对应记录当前的undo log

  • deleted_bit,删除标记位,删除时设置

undo log

  • 用于行记录回滚,同时用于实现MVCC


    图片1.png

2.2 操作方式

  1. update

  • 行记录数据写入undo log,事务的回滚操作就需要undo log

  • 更新行记录数据,当前事务ID写入db_trx_id,undo log指针写入db_roll_ptr

  1. delete

  • 和update一样,只增加deleted_bit设置

  1. insert

  • 生成undo log

  • 插入行记录数据,当前事务ID写入db_trx_id, db_roll_ptr为空

这样设计使得读操作很简单,性能很好,并且也能保证只会读到符合标准的行,不足之处是每行记录都需要额外的储存空间,需要做更多的行检查工作,以及额外的维护工作

2.3 MVCC如何实现RR

  • RR定义:在一个事务内同一快照读执行任意次数,得到的数据一致;且只能读到第一次执行前已经提交的数据或本事务内更改的数据

  • 原理:对符合查询条件的记录进行可见性判断(就是那些数据本事务可以看见,那些数据看不见

  • read view:记录当前处于活动状态的所有事务ID,RR级别下,第一次快照读时创建,RC级别下,每次快照读均会创建新的

  • 缺点: 可能出现幻读

图片2.png



图片3.png


3 总结

在事务隔离级别为RC和RR级别下, InnnoDB存储引擎使用的才是多版本并发控制。然而,对于快照数据的定义却不相同。在RC事务隔离级别下,对于快照数据(undo端数据),总是读取被锁定行的最新的一份快照数据。而在RR事务隔离级别下,对于快照数据,多版本并发控制总是读取事务开始时的行数据。

 


以上是关于INNODB MVCC的主要内容,如果未能解决你的问题,请参考以下文章

MySQL-InnoDB-MVCC多版本并发控制

MySQL-InnoDB-MVCC多版本并发控制

Innodb MVCC的实现分析

MySQL中InnoDB的多版本并发控制(MVCC)

MySQL——MySQL InnoDB的MVCC实现机制

MySQL——MySQL InnoDB的MVCC实现机制