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

相关推荐
万联WANFLOW6 分钟前
外贸独立站:服务器放海外国内后台卡,放国内海外客户打不开——这道两头难怎么破?
运维·网络·数据库·经验分享
金海境科技23 分钟前
【服务器数据恢复】H3C CAS虚拟化平台HP存储qcow2文件损坏快照丢失数据恢复案例
服务器·数据库·经验分享
Now喔30 分钟前
mysql-笔记一
mysql
正儿八经的少年37 分钟前
Spring 事务保证数据一致性
java·数据库·spring
foo1st41 分钟前
MySQL 8.0(Windows)升级笔记
数据库·笔记·mysql
张人玉44 分钟前
基于 Vue 3 + ECharts + Express + SQLite 的可视化大屏与业务管理系统——YOLOv8 高精度车辆行人检测与计数系统
数据库·vue.js·yolo·sqlite·echarts·express
吴声子夜歌1 小时前
MongoDB 4.x——高级特性(Change Stream和事务)
数据库·mongodb
是店小二呀1 小时前
NanoPi R4S怎么搭私人云盘?iStoreOS与WebDAV完整教程
数据库·人工智能
Database_Cool_1 小时前
云数据库备份是怎么实现的、支持时间点恢复吗:阿里云 RDS MySQL 备份与 PITR 详解
数据库·mysql·阿里云
名字还没想好☜1 小时前
Go 的 select 实战:超时、非阻塞收发与优雅退出的三个套路
开发语言·数据库·golang·go·并发