MySql,MVCC实现及其机制,快照读在RC,RR下的区别说明
一、什么是MVCC
多版本并发控制
Multiversion Concurrency Control
大部分的MySQL的存储 引擎,比如InnoDB,Falcon,以及PBXT并不是简简单单的使用行锁机制。它们都使用了行锁结合一种提高并发的技术,被称为MVCC(多版本并 发控制)。MVCC并不单单应用在MySQL中,其他的数据库如Oracle,PostgreSQL,以及其他数据库也使用这个技术。
MVCC避免了许多需要加锁的情形以及降低消耗。这取决于它实现的方式,它允许非阻塞读取,在写的操作的时候阻塞必要的记录。
MVCC保存了某一时刻数据的一个快照。意思就是无论事物运行了多久,它们都能看到一致的数据。也就是说在相同的时间下,不同的事物看相同表的数据是不同的。如果你从来没有这方面的经验,可能说这些有点令人困惑。但是在以后这个会很容易理解和熟悉的。
每个存储引擎实现MVCC方式都是不同的。有许多种包含了乐观(optimistic)和悲观(pessimistic)的并发控制。我们用简单的InnoDb的行为来举例说明MVCC工作方式。
InnoDB实现MVCC的方法是,它存储了每一行的两个额外的隐藏字段,这两个隐藏字段分别记录了行的创建的时间和删除的时间。在每个事件发生的时 候,每行存储版本号,而不是存储事件实际发生的时间。每次事物的开始这个版本号都会增加。自记录时间开始,每个事物都会保存记录的系统版本号。依照事物的 版本来检查每行的版本号。在事物隔离级别为可重复读的情况下,来看看怎样应用它。
SELECT
- InnoDB检查每行,要确定它符合两个标准。
- InnoDB必须知道行的版本号,这个行的版本号至少要和事物版本号一样的老。(也就是是说它的版本号可能少于或者和事物版本号相同)。这个既能确定事物开始之前行是存在的,也能确定事物创建或修改了这行。
- 行的删除操作的版本一定是未定义的或者大于事物的版本号。确定了事物开始之前,行没有被删除。
- 符合了以上两点。会返回查询结果。
INSERT
- InnoDB记录了当前新增行的系统版本号。
DELETE
- InnoDB记录的删除行的系统版本号作为行的删除ID。
UPDATE
- InnoDB复制了一行。这个新行的版本号使用了系统版本号。它也把系统版本号作为了删除行的版本。
所有其他记录的结果保存是,从未获得锁的查询。这样它们查询的数据就会尽可能的快。要确定查询行要遵循这些标准。缺点是存储引擎要为每一行存储更多的数据,检查行的时候要做更多的处理以及其他内部的一些操作。
MVCC只能在可重复读和可提交读的隔离级别下生效。不可提交读不能使用它的原因是不能读取符合事物版本的行版本。它们总是读取最新的行版本。可序列化不能使用MVCC的原因是,它总是要锁定行。
下面的表说明了在MySQL中不同锁的模式以及并发级别。
我们在操作数据库的时候总是这四大类 读读 读写 写读 写写,读读肯定是没有任务数据问题的,但对事物有了解的同学就会知道,读写、写写操作很容易就会导致数据不一致。
在此之前解决这类问题的常用方式就是 加锁,听名字就知道这是个很复杂、很耗性能的操作,所以大神们不满足这个操作,从而在MySQL里面实现了MVCC。
MVCC并不是MySQL独有的,它是一个理念,百度百科解释如下:
Multi-Version Concurrency Control 多版本并发控制,MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问;在编程语言中实现事务内存。
MVCC里面有一些关键词,理解这些关键词,你就明白了什么是MVCC。MVCC是解决读写、写读导致数据不一致的问题,写写问题还是需要加锁来解决。
所以我们可以使用 MVCC + 锁(乐观锁/悲观锁)来解决全部的问题。
二、当前读、快照读
当前读就是读取最新的数据,为了保证读取的是最新且准确的数据,所以它在读取的时候会加锁,防止其它事物操作。
快照读是不加锁的方式,当一个事物要操作数据库的时候,会在这个事物的基础上形成一个快照,其它的操作就读取这个快照。
MVCC就是基于快照读来实现的,在MySQL里面的快照读是基于这样几个关键点来实现的
- 三个隐藏参数 (DB_ROW_ID 隐藏主键id、DB_ROLL_PTR 回滚指针、DB_TRX_ID 事物id)
- undo log 日志
- read view
三、隐藏字段
假如我们有一张表,里面有两个字段,name、age,但实际上我们表里的数据是这样的
3-1、隐藏主键
6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引
聚簇索引:数据存储和索引是存在一起的,逻辑上和物理上都是一起的,一个表只能有一个聚簇索引。
注:理解聚簇索引可以很好的理解MySQL的索引规则,感兴趣的可以看看这个 MySQL索引详解
3-2、事物id
记录这条记录最后一次操作的事物id
3-3、回滚指针
回滚指针,指向这条记录的上一个版本(存储于rollback segment里),用于配合下面的 undo log。
四、undo log
undo log 日志分为两种
(1)、 insert undo log 数据库在插入数据的时候产生,只有在当前事物回滚的时候才有用,所以在当前事物结束的时候它就没用了,就会被删除。
(2)、 update undo log 数据库在更新、删除的时候产生,除了当前事物会使用,在快照读的时候也会使用,所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除。
五、Read View
什么是读视图呢?数据库的操作都是多个事物同时进行的,有读有写。假如当前有两个事物,A事物读取,B事物正在更新数据。
在A事物开始的时候,就形成当前数据库的一个快照,记录并维护系统当前活跃事务的ID。read view 主要是用来做可见性判断的,它会判断每条记录的的数据,这条数据可能是真实的数据,也可能是undo log 中的数据。
read view 用一个可见性的算法,来判断当前是读取真实的数据,还是undo log的数据。这里可以简单理解read view 内部维护了一个事物id列表,里面有最大值和最小值,可以判断其它事物的id是否在这个可见范围内。
N、其它
N-1、快照读在RC和RR下的区别
- RC (read-committed)读已提交, 可能会导致 不可重复读、幻读
- RR (repeatable-read)可重复读,可能会导致 不可重复读
幻读 : 事物A查询数据库查询出来了20条数据,然后事物B删除了2条数据,这时候事物A再去查询发现只有18条了,从而产生了幻觉。
我们知道在RR级别下面不会产生幻读,之所以不会产生幻读,是快照读在RC和RR下的生成的策略不一样。
RC隔离级别下,是每个快照读都会生成并获取最新的Read View;而在RR隔离级别下,则是同一个事务中的第一个快照读才会创建Read View, 之后的快照读获取的都是同一个Read View。
MySQL/InnoDB定义的4种隔离级别:
Read Uncommited
- 可以读取未提交记录。
Read Committed (RC)
- 针对当前读,RC隔离级别保证对读取到的记录加锁 (记录锁),存在幻读现象
Repeatable Read (RR)(默认)
- 针对当前读,RR隔离级别保证对读取到的记录加锁 (记录锁),同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入 (间隙锁),不存在幻读现象。
Serializable
- 从MVCC并发控制退化为基于锁的并发控制。不区别快照读与当前读,所有的读操作均为当前读,读加读锁 (S锁),写加写锁 (X锁)。
- Serializable隔离级别下,读写冲突,因此并发度急剧下降,在MySQL/InnoDB下不建议使用。
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
相关文章
InnoDb 体系架构和特性详解 (Innodb存储引擎读书笔记总结)
下面小编就为大家带来一篇InnoDb 体系架构和特性详解 (Innodb存储引擎读书笔记总结)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧2017-03-03MYSQL拒绝访问报错not allowed to connect
MYSQL拒绝访问报错not allowed to connect,下面有个可行的方法,可以在其它任何的主机上以root身份登录2014-07-07
最新评论