快照读(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 锁,则会被阻塞。指针
多版本并发控制技术的英文全称是:Multiversion Concurrency Control,简称 MVCC,是经过保存数据的历史版本,经过对数据行的多个版本管理来实现数据库的并发控制。这样咱们就能够经过比较版本号决定数据是否显示出来,读取数据的时候不须要加锁也能够保证事务的隔离效果(能够理解成乐观锁)。code
多版本并发控制(MVCC)只在可重复读(REPEATABLE READ)和提交读(READ COMMITTED)两个隔离级别下工做,其余两个隔离级别都和 MVCC 不兼容,由于未提交读(READ UNCOMMITTED),老是读取最新的数据行,而不是符合当前事务版本的数据行;而可串行化(SERIALIZABLE) 则会对全部读取的行都加锁。blog
MySQL 的大多数事务型存储引擎实现的都不是简单的行级锁。基于提高并发性能的考虑,它们通常都同时实现了多版本并发控制(MVCC)。不只是 MySQL,包括 Oracle、PostgreSQL 等其余数据库系统也都实现了 MVCC,但各自的实现机制不尽相同,由于 MVCC 没有一个统一的实现标准,典型的有乐观(optimistic)并发控制和悲观(pessimistic)并发控制。索引
经过 MVCC 可让读写互相不阻塞,即读不阻塞写,写不阻塞读,这样就能够提高事务并发处理能力。
提升并发的演进思路:
- 普通锁,只能串行执行;
- 读写锁,能够实现读读并发;
- 数据多版本并发控制,能够实现读写并发。
由于 InnoDB 的 MVCC 采用了乐观锁的方式,读取数据时并不须要加锁,对于写操做,也只锁定必要的行。
一致性非锁定读也被称为快照读,这也是 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)