说下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是通过间隙锁来控制的,简单来说就是锁住这个范围内的数据,不给插入,这样来解决幻读的写问题。

相关推荐
老徐电商数据笔记2 小时前
BI工具与数据分析平台:数据价值呈现的最后一公里
数据库·数据挖掘·数据分析·bi·bi选型思考
码农水水3 小时前
米哈游Java面试被问:机器学习模型的在线服务和A/B测试
java·开发语言·数据库·spring boot·后端·机器学习·word
酉鬼女又兒4 小时前
SQL24 统计每个用户的平均刷题数
数据库·sql·mysql
雷工笔记4 小时前
数据库|SQLServer2025安装教程
数据库·sqlserver
一只自律的鸡4 小时前
【MySQL】第六章 子查询
数据库·mysql
Knight_AL5 小时前
Spring Boot 事件机制详解:原理 + Demo
java·数据库·spring boot
野人李小白5 小时前
DBeaver 界面友好,支持多种数据库,具备强大的 SQL 编辑、可视化查询、数据迁移及插件扩展功能,是开发者首选的数据库管理工具。
数据库·sql
山峰哥5 小时前
SQL索引优化实战:3000字深度解析查询提速密码
大数据·数据库·sql·编辑器·深度优先
观音山保我别报错6 小时前
消息队列项目基础知识总结
linux·服务器·数据库