| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 大数据 -> Mysql 【MVCC】 -> 正文阅读 |
|
[大数据]Mysql 【MVCC】 |
MVCCMVCC是在并发访问数据库时,通过对数据做多版本管理,避免因为写锁的阻塞而造成读数据的并发阻塞问题。 通俗的讲就是MVCC通过保存数据的历史版本,根据比较版本号来处理数据的是否显示,从而达到读取数据的时候不需要加锁就可以保证事务隔离性的效果。 实现原理每次事务开启前都会从数据库获得一个自增长的事务ID,可以从事务ID判断事务的执行先后顺序。 在innodb 中每个事务开启后都会得到一个read_view。 副本主要保存了当前数据库系统中正处于活跃(没有commit)的事务的ID号,其实简单的说这个副本中保存的是系统中当前不应该被本事务看到的其他事务id列表。 Read view 的几个重要属性
数据对于当前事务显示不显示需要根据 ReadView 进行条件匹配来决定显示哪个版本的数据。 (1)数据事务ID <up_limit_id 则显示 如果数据事务ID小于read view中的最小活跃事务ID,则可以肯定该数据是在当前事务启之前就已经存在了的,所以可以显示。 (2)数据事务ID>=low_limit_id 则不显示 如果数据事务ID大于read view 中的当前系统的最大事务ID,则说明该数据是在当前read view 创建之后才产生的,所以数据不予显示。 (3) up_limit_id <=数据事务ID<low_limit_id 则与活跃事务集合trx_ids里匹配 如果数据的事务ID大于最小的活跃事务ID,同时又小于等于系统最大的事务ID,这种情况就说明这个数据有可能是在当前事务开始的时候还没有提交的。 所以这时候我们需要把数据的事务ID与当前read view 中的活跃事务集合trx_ids 匹配: 情况1: 如果数据事务ID不存在于trx_ids 集合(则说明read view产生的时候事务已经commit了),这种情况数据则可以显示。 情况2: 如果事务ID存在trx_ids则说明read view产生的时候数据还没有提交,但是如果数据的事务ID等于creator_trx_id ,那么说明这个数据就是当前事务自己生成的,自己生成的数据自己当然能看见,所以这种情况下此数据也是可以显示的。 情况3: 如果事务ID既存在trx_ids而且又不等于creator_trx_id那就说明read view产生的时候数据还没有提交,又不是自己生成的,所以这种情况下此数据不能显示。 (4)不满足read view条件时候,从undo log里面获取数据 当数据的事务ID不满足read view条件时候,从undo log里面获取数据的历史版本,然后数据历史版本事务号回头再来和read view 条件匹配 ,直到找到一条满足条件的历史数据,或者找不到则返回空结果; MVCC 只实现读已提交【READ_COMMITTED】、可重复读【REPEATABLE_READ】隔离级别下。 RC(read commit) 级别下同一个事务里面的每一次查询都会获得一个新的read view副本。这样就可能造成同一个事务里前后读取数据可能不一致的问题(重复读)。 RR(重复读)级别下的一个事务里只会获取一次read view副本,从而保证每次查询的数据都是一样的。 快照读 快照读是指读取数据时不是读取最新版本的数据,而是基于历史版本读取的一个快照信息(mysql读取undo log历史版本) ,快照读可以使普通的SELECT 读取数据时不用对表数据进行加锁,从而解决了因为对数据库表的加锁而导致的两个如下问题 1、解决了因加锁导致的修改数据时无法对数据读取问题; 2、解决了因加锁导致读取数据时无法对数据进行修改的问题; 当前读 当前读是读取的数据库最新的数据,当前读和快照读不同,因为要读取最新的数据而且要保证事务的隔离性,所以当前读是需要对数据进行加锁的(Update delete insert ,select …lock in share mode select for update 为当前读) 更新数据 update 语句 先读后写的,而这个读就是当前读。 除了 update 语句外,select 语句如果加锁,也是当前读。
当前读必须要读最新的版本,需要获得锁,如果另一个事务B正在update,占据了写锁,还没有提交,那么事务A的 update (当前读)就会被阻塞住。直到事务B commit 释放锁之后,事务 A 的 update 才可以继续。 行锁的
一致性读(快照读):依赖MVCC 、Undolog 实现 当前读:依赖行锁实现
这是因为表结构没有对应的行数据,也没有 row trx_id,因此只能遵循当前读的逻辑。 当然,MySQL 8.0 已经可以把表结构放在 InnoDB 字典里了,也许以后会支持表结构的可重复读。 8.0已经把表结构放到InnoDB字典里,表结构支持可重复读。在mysqldump过程中修改表结构并不会导致程序终止了。已测!
快照读的情况下可以避免幻读问题,在当前读的情况下则需要使用间隙锁来解决幻读问题的。 ? 快照读因为插入的数据事务ID大于等于 readview里的最大活跃事务ID。所以当前事务是看不到新插入的数据的。 ? 但是当前读的话就不同了,当前读每次都会读取最新的数据。所以两次读取中间如果可以插入数据,那么就肯定会造成幻读问题。所以使用间隙锁将查询范围锁起来,避免数据插入。
情况一:这种情况下就可以产生上述的结果。 ? 用另外一个事物在update执行之前,先把所有c值修改,应该就可以。比如update t set c = id + 1。因为是可重复读隔离级别,在begin的时候创建了视图,如果没有事务B,事务A在更新完之后,会将数据的事务id设为自己的事务id,这样下一个查询语句可以看到自己的更新结果。 ? 但是因为有了事务B的更新,将事务A的更新阻塞了(行锁),事务B更新完之后,事务A更新的匹配条件不成立了,导致更新 0 rows affected。下一个查询事务也因为数据事务id已经是事务B的事务id,而且事务B是晚于事务A开启的,事务B修改的数据处于高水位中,事务A看不到新更新的数据,只能根据undo log 去找之前的数据。 情况二:session B’ 先于 sessionA 因为上述的数据可见性的情况3 如果事务ID既存在trx_ids而且又不等于creator_trx_id那就说明read view产生的时候数据还没有提交,又不是自己生成的,所以这种情况下此数据不能显示。 出现这种在同一个事务中再次select还是旧值,就会出现“明明值没变可就是更新不了”的“异象”(anomaly)。 解决方案就是每次cas更新不管成功失败,结束当前事务。如果失败则重新起一个事务进行查询更新。
极客时间《Mysql 45 讲》 |
|
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 | -2025/1/16 1:57:42- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |