说下Mysql的MVCC机制

在sql标准下只有串行化的隔离级别才能解决幻读,但是这种读写都互斥的行为肯定影响性能,MVCC大名鼎鼎是因为它可以在不可重复读的隔离级别下"杜绝了幻读的问题",大大增加了mysql的读写并发性能。

额,"杜绝了幻读的问题"这里为什么打引号?首先我个人理解的幻读分为两种情况,事务A查询某个范围的数据,(注意是范围,具体某条就是重复读的问题了),事务B这时在这个范围插入了数据并提交了事务,那么事务A这时再次查询这个范围,数据条数对不上了(产生了幻觉),这个我称之为幻读的读问题;还是一样,事务A查询某个范围的数据,事务B这时在这个范围插入了数据并提交了事务,然后事务A也插入了同样的数据,插入失败(也产生了幻觉),我称这个为幻读的写问题。为什么打引号,是因为MVCC只解决了幻读的读问题。

弄清MVCC机制首先要先了解下undo log,undo log用于事务回滚的,事务提交后,按理说不用回滚了,可以删了,但是它不会删,这就是留给MVCC用的,但是insert 的undo log会删,这个影响不大,因为它创造了数据,不会有并发的问题(写、写肯定时互斥的)。那么这个undo log 不删除就记录一个从1递增的号码,每次修改后面的undo会记录前面undo log的号码,形成了一个undo log链表。

另外一个就是MYSQL每条数据有三个隐藏字段:

DB_ROW_ID: 如果没有为表显式的定义主键,并且表中也没有定义唯一索引,那么InnoDB会自动为表添加一个row_id的隐藏列作为主键。

DB_TRX_ID︰每个事务都会分配一个事务ID,当对某条记录发生变更时,就会将这个事务的事务ID写入trx_id中。

DB_ROLL_PTR:回滚指针,本质上就是指向undo log的指针。(一个事务多个操作的话,undo log就与多条,它里面存的一个字段就指向上一条undolog,这样就形成了一个链表,回滚指针记录的最新的undo log的指针)

通过DB_ROLL_PTR就能找到undo log链表了。如果空的说明没有并发,就你查,很简单,直接查表就好;那么链表不为空说明有别的事务在修改或者已经修改好了,那我们就从这个链表倒着找我们要是的数据,那这个数据能不能被查看到呢?这个就需要依靠readview了,

MVCC的机制就是一个事务开启后,这个瞬间会生成一个readview这里还有一个东西叫ReadView ,这个ReadView中主要包含 4 个比较重要的内容,分别如下:

  1. creator_trx_id,创建这个 Read View 的事务 ID。

    说明:只有在对表中的记录做改动时(执行INSERT、DELETE、UPDATE这些语句时)才会为事务分配事务id,否则在一个只读事务中的事务id值都默认为 0 。

  2. trx_ids,表示在生成ReadView时当前系统中活跃的读写事务的事务id列表

  3. up_limit_id,活跃的事务中最小的事务 ID(什么是活跃?就是还没提交或者回滚的事务)。

  4. low_limit_id,表示生成ReadView时系统中应该分配给下一个事务的id值。low_limit_id 是系统最大的事务id值,这里要注意是系统中的事务id,需要区别于正在活跃的事务ID。

注意:low_limit_id并不是trx_ids中的最大值,事务id是递增分配的。比如,现在有id为 1 ,2 , 3 这三个事务,之后id为 3 的事务提交了。那么一个新的读事务在生成ReadView时,trx_ids就包括 1 和 2 ,up_limit_id的值就是 1 ,low_limit_id的值就是 4 。

ReadView的规则

有了这个ReadView,这样在访问某条记录时,只需要按照下边的步骤判断记录的某个版本是否可见。

  • 如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。

  • 如果被访问版本的trx_id属性值小于ReadView中的up_limit_id值,表明生成该版本的事务在当前事务生成ReadView前已经提交,所以该版本可以被当前事务访问。

  • 如果被访问版本的trx_id属性值大于或等于ReadView中的low_limit_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。

  • 如果被访问版本的trx_id属性值在ReadView的up_limit_id和low_limit_id之间,那就需要判断一下trx_id属性值是不是在trx_ids列表中。

  • 如果在,说明创建ReadView时生成该版本的事务还是活跃的,该版本不可以被访问。

  • 如果不在,说明创建ReadView时生成该版本的事务已经被提交,该版本可以被访问

但还是有幻读的写问题,这个mvcc管不了,innodb是通过间隙锁来控制的,简单来说就是锁住这个范围内的数据,不给插入,这样来解决幻读的写问题。

相关推荐
Databend4 小时前
从湖仓升级为 Agent 时代的数据控制面,Snowflake 和 Databricks 有哪些布局
大数据·数据库·agent
ClouGence7 小时前
SQL Server CDC 能放到 Always On 备库读吗?一文讲透原理与实践
数据库·sql server
先吃饱再说1 天前
存储的进化:从 MySQL 到浏览器缓存,数据到底住在哪?
数据库
Nturmoils1 天前
字段太多看不全,ksql 的展开模式和输出控制怎么用
数据库·后端
Databend1 天前
Agent 轨迹分析与归因的数据工程实践
大数据·数据库·agent
这个DBA有点耶1 天前
SQL改写进阶:标量子查询的“隐形代价”与消除实战
数据库·mysql·架构
smallyoung1 天前
数据库乐观锁深度解析:MySQL、PostgreSQL 实战 + Spring Boot 集成指南
数据库·mysql·postgresql
parade岁月1 天前
MySQL JOIN解析:朴实无华但食之有味
数据库·后端
用户3169353811831 天前
MySQL服务无法启动问题解决全记录
数据库
vivo互联网技术1 天前
从 10 分钟到 1 秒:ES 深度分页任意跳页的三轮优化实战
服务器·数据库·redis·elasticsearch·深度分页