| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 大数据 -> MVCC如何解决不可重复读和读取未提交 -> 正文阅读 |
|
[大数据]MVCC如何解决不可重复读和读取未提交 |
参考:《MySQL是怎样运行的:从根儿上理解MySQL》 目录 MVCC原理MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。MVCC在MySQL InnoDB中的实现主要是为了提高数据库并发性能,用更好的方式去处理读写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。 那么MVVC是怎么实现的呢,要理解其底层原理,需要知道两个概念,一个是版本链,表示某一个数据被修改的记录,比如某一条数据被事务1、事务2,、事务50依次修改。 一个是readview,表示在执行查询语句时会生成这样一个readview,里面有当前正在执行的事务有哪些,那么这些事务对应版本的数据我就不读取,这样不加锁我也知道哪些数据是还没有被提交的,顺着版本链我就知道哪些数据我可以读取,哪些不能读取。 详解如下 版本链对于使用 InnoDB 存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列:
trx_id :
每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的
事务id
赋值给
trx_id
隐藏列。
roll_pointer
:每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到
undo日志
中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息
每次对记录进行改动,都会记录一条
undo日志
,每条
undo日志
也都有一个
roll_pointer
属性
,可以将这些
undo日志
都连起来,串成一个链表,所以现在的情况就像下图一样:
?这个版本链中 有 80, 100 ,200 这三个事务,当然这三个事务80的可能已经提交结束了,200的和100的可能还没有,那么200和100 就属于活跃事务。
对该记录每次更新后,都会将旧值放到一条
undo日志
中,就算是该记录的一个旧版本,随着更新次数的增多,所有的版本都会被
roll_pointer
属性连接成一个链表,我们把这个链表称之为
版本链
,
版本链的头节点就是当前记录最新的值
。另外,每个版本中还包含生成该版本时对应的
事务id。
ReadView
ReadView
的概念,这个
ReadView
中主要包含
4
个比较重要的内容:
直接看下面的例子比较好理解
m_ids
:表示在生成
ReadView时当前系统中活跃的读写事务的
事务id
列表。
min_trx_id:表示在生成
ReadView
时当前系统中活跃的读写事务中最小的
事务id
,也就是
m_ids
中的最小值。
max_trx_id:表示生成
ReadView
时系统中应该分配给下一个事务的
id
值。
creator_trx_id
:表示生成该
ReadView
的事务的
事务id
。
注意max_trx_id并不是m_ids中的最大值,事务id是递增分配的。比方说现在有id为1,2,3这三
个事务,之后id为3的事务提交了。那么一个新的读事务在生成ReadView时,m_ids就包括1和2,min_trx_id的值就是1,max_trx_id的值就是4。
只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会
为事务分配事务id,否则在一个只读事务中的事务id值都默认为0。
举例
假设一个表中,初始一个事务insert一条记录,名字是张一,事务提交,叫它事务A,事务id是1.
又来了事务2,修改名字为张二,但是还没有提交,属于活跃事务, 又来了事务3,修改名字为张三,也没有提交,属于活跃事务。 读取已提交这时候又一个事务启动,执行了查询语句,因为只有查询,所以事务id为0, 那么首先会创建一个readview,里面有几项信息,比如当前活跃事务id表 ,这个表里面有2,3, 通过这个就知道2,3还没有提交,那么就不能读取这两个版本对应的名字,说不定后来它们又改成什么样子,而是按照版本链找,知道找到1,因为1不在活跃的事务id表中,所以读取这个版本对应的记录,读到“张一”。 如果之后事务2提交了,那么还是这个事务在读取的时候,就会新创建一个readview,这次里面的活跃id表中就没有事务2了,那么读到的就是“张二”。 可重复读这时候又一个事务启动,执行了查询语句,因为只有查询,所以事务id为0, 那么首先会创建一个readview,里面有几项信息,比如当前活跃事务id表 ,这个表里面有2,3, 通过这个就知道2,3还没有提交,那么就不能读取这两个版本对应的名字,说不定后来它们又改成什么样子,而是按照版本链找,知道找到1,因为1不在活跃的事务id表中,所以读取这个版本对应的记录,读到“张一”。 如果之后事务2提交了,那么还是这个事务在读取的时候,不会新创建一个readview,这次里面的活跃id表中依旧是事务2,3,那么读到的依然是是“张一”,这样就实现了可重复读。 总结READ COMMITTD 、REPEATABLE READ 这两个隔离级别的一个很大不同就是:生成ReadView的时机不同,READ COMMITTD在每一次进行普通SELECT操作前都会生成一个ReadView,而REPEATABLE READ只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复使用这个ReadView就好了。 有了这个 ReadView ,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见: 如果被访问版本的 trx_id 属性值与 ReadView 中的 creator_trx_id 值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。 如果被访问版本的 trx_id 属性值小于 ReadView 中的 min_trx_id值,表明生成该版本的事务在当前事务生成 ReadView 前已经提交,所以该版本可以被当前事务访问。 如果被访问版本的 trx_id 属性值大于 ReadView 中的 max_trx_id值,表明生成该版本的事务在当前事务生成 ReadView 后才开启,所以该版本不可以被当前事务访问。 如果被访问版本的 trx_id 属性值在 ReadView 的 min_trx_id 和 max_trx_id 之间,那就需要判断一下trx_id 属性值是不是在 m_ids 列表中,如果在,说明创建 ReadView 时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建 ReadView 时生成该版本的事务已经被提交,该版本可以被访问。 如果某个版本的数据对当前事务不可见的话,那就顺着版本链找到下一个版本的数据,继续按照上边的步骤判断可见性,依此类推,直到版本链中的最后一个版本。如果最后一个版本也不可见的话,那么就意味着该条记录对该事务完全不可见,查询结果就不包含该记录。 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 | -2024/11/24 6:05:46- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |