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

1、快照读与当前读

快照读(SnapShot Read) 是一种一致性不加锁的读,是 InnoDB 并发如此之高的核心缘由之一。数据库

在 READ COMMITTED 事务隔离级别下,一致性不加锁的读是指,老是读取被锁定行的最新一份快照数据,所以其它事务修改了该行数据,该事务也能读取到,这也贴合了 RC 隔离级别下容许不可重复读的问题;segmentfault

在 REPEATABLE READ 事务隔离级别下,一致性不加锁的读是指,事务读取到的数据,要么是事务开始前就已经存在的数据,要么是事务自身插入或者修改过的数据。(下面将以此隔离级别说明);并发

不加锁的简单的 SELECT 都属于快照读,例如:mvc

SELECT * FROM t WHERE id=1;

与快照读相对应的则是当前读(Current Read),当前读就是读取最新数据,而不是历史版本的数据。加锁的 SELECT 就属于当前读,例如:高并发

SELECT * FROM t WHERE id=1 LOCK IN SHARE MODE;
SELECT * FROM t WHERE id=1 FOR UPDATE;

SELECT...FOR UPDATE 对读取的行记录加一个 X 锁,其它事务不能对已锁定的行加上任何锁。性能

SELECT...LOCK IN SHARE MODE 对读取的行记录加一个 S 锁,其它事务能够向被锁定的行加 S 锁,可是若是加 X 锁,则会被阻塞。指针

2、基于快照读的多版本并发控制

多版本并发控制技术的英文全称是:Multiversion Concurrency Control,简称 MVCC,是经过保存数据的历史版本,经过对数据行的多个版本管理来实现数据库的并发控制。这样咱们就能够经过比较版本号决定数据是否显示出来,读取数据的时候不须要加锁也能够保证事务的隔离效果(能够理解成乐观锁)。code

多版本并发控制(MVCC)只在可重复读(REPEATABLE READ)和提交读(READ COMMITTED)两个隔离级别下工做,其余两个隔离级别都和 MVCC 不兼容,由于未提交读(READ UNCOMMITTED),老是读取最新的数据行,而不是符合当前事务版本的数据行;而可串行化(SERIALIZABLE) 则会对全部读取的行都加锁。blog

MySQL 的大多数事务型存储引擎实现的都不是简单的行级锁。基于提高并发性能的考虑,它们通常都同时实现了多版本并发控制(MVCC)。不只是 MySQL,包括 Oracle、PostgreSQL 等其余数据库系统也都实现了 MVCC,但各自的实现机制不尽相同,由于 MVCC 没有一个统一的实现标准,典型的有乐观(optimistic)并发控制和悲观(pessimistic)并发控制。索引

3、多版本并发控制解决了哪些问题?

1. 读写之间阻塞的问题

经过 MVCC 可让读写互相不阻塞,即读不阻塞写,写不阻塞读,这样就能够提高事务并发处理能力。

提升并发的演进思路:

  • 普通锁,只能串行执行;
  • 读写锁,能够实现读读并发;
  • 数据多版本并发控制,能够实现读写并发。

2. 下降了死锁的几率

由于 InnoDB 的 MVCC 采用了乐观锁的方式,读取数据时并不须要加锁,对于写操做,也只锁定必要的行。

3. 解决一致性读的问题

一致性非锁定读也被称为快照读,这也是 InnoDB 存储引擎的默认读取方式,当咱们查询数据库在某个时间点的快照时,只能看到这个时间点以前事务提交更新的结果,而不能看到这个时间点以后事务提交的更新结果。

4、InnoDB 的 MVCC 是如何工做的?

1. InnoDB 是如何存储记录的多个版本的?

事务版本号: 每开启一个事务,咱们都会从数据库中得到一个事务 ID(也就是事务版本号),这个事务 ID 是自增加的,经过 ID 大小,咱们就能够判断事务的时间顺序。

行记录的隐藏列: InnoDB 的叶子段存储了数据页,数据页中保存了行记录,而在行记录中有一些重要的隐藏字段:

  • DB_ROW_ID:6-byte,隐藏的行 ID,用来生成默认聚簇索引。若是咱们建立数据表的时候没有指定聚簇索引,这时 InnoDB 就会用这个隐藏 ID 来建立汇集索引。采用聚簇索引的方式能够提高数据的查找效率。
  • DB_TRX_ID:6-byte,操做这个数据的事务 ID,也就是最后一个对该数据进行插入或更新的事务 ID。(InnoDB 的插入、更新、删除都会更新该事务 ID,同时删除会将一个特殊位标记为已删除)
  • DB_ROLL_PTR:7-byte,回滚指针,也就是指向这个记录的 Undo Log 信息。

Undo Log: InnoDB 将行记录快照保存在了 Undo Log 里,咱们能够在回滚段中找到它们,以下图所示,回滚指针将数据行的全部快照记录都经过链表的结构串联了起来,每一个快照的记录都保存了当时的 db_trx_id,也是那个时间点操做这个数据的事务 ID。这样若是咱们想要找历史快照,就能够经过遍历回滚指针的方式进行查找。



参考连接:MySQL的多版本并发控制(MVCC)

相关文章
相关标签/搜索