前言MySQL 是目前流行的开源数据库之一,各大公司都使用 MySQL 作为自家的关系型数据库,但是 MySQL 作为一个数据库而言,基本使用是非常简单的,只要会一点点建表语句(可以使用工具建表),一点点查询语句就可以使用 MySQL 来存储数据了 。
这种没有灵魂的操作,对于很多初学者来说也许已经是家常便饭了 。但是对于一些已经有开发经验的人来说,这是远远不够的 。你必须要学习很多数据库相关的知识,而这一篇就是彻底来剖析 MySQL 中的 MVCC 是如何实现的 。看完这篇文章,你就可以知道各种隔离级别之下,MVCC 的作用是什么?MVCC 在什么时候会使用?怎么使用?
示例表CREATE TABLE `test`.`Untitled`(`id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,`phone` char(11) NOT NULL,`name` varchar(255) NOT NULL,`age` int(3) NOT NULL,`country` varchar(255) NOT NULL,PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `uk_phone`(`phone`) USING BTREE,INDEX `idx_name`(`name`) USING BTREE) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;复制代码
insert into person values (null, '1351111111', 'any', 20, '蜀');insert into person values (null, '1351111112', 'bat', 21, '吴');复制代码
id phone name age country 1 1351111111 any 20 蜀 2 1351111112 bat 21 吴
MVCCMVCC 是无锁操作的一种实现方式,无锁就是没有锁 。无锁能够大幅度提高数据库的并发性 。它最基本的表现形式就是一致性非锁定读,通过 MVCC (多版本并发控制)来实现 。MVCC 主要又是依靠 Read View 来实现的 。
在数据库中的每一条记录实际都会存在三个隐藏列:
- DB_TRX_ID:该列表示此记录的事务 ID
- DB_ROLL_PTR:该列表示一个指向回滚段的指针,实际就是指向该记录的一个版本链
- DB_ROW_ID:记录的 ID,如果有指定主键,那么该值就是主键 。如果没有主键,那么就会使用定义的第一个唯一索引 。如果没有唯一索引,那么就会默认生成一个值 。
start transaction;update person set age = 22 where id = 1;update person set name = 'out' where id = 1;commit;复制代码
当执行完上面两条语句之后,但是还没有提交事务之前,它的版本链如下图所示:文章插图
而 Read View 是用来判断每一个读取语句有资格读取版本链中的哪个记录 。所以在在读取之前,都会生成一个 Read View 。然后根据生成的 Read View 再去读取记录 。
在事务中,只有执行插入、更新、删除操作时才会分配到一个事务 id 。如果事务只是一个单纯的读取事务,那么它的事务 id 就是默认的 0 。Read View 的结构如下:
- rw_trx_ids:表示在生成 Read View 时,当前活跃的读写事务数组 。
- min_trx_id:表示在生成 Read View 时,当前已提交的事务号 + 1,也就是在 rw_trx_ids 中的最小事务号 。
- max_trx_id:表示在生成 Read View 时,当前已分配的事务号 + 1,也就是将要分配给下一个事务的事务号 。
- curr_trx_id:创建 Read View 的当前事务 id 。
文章插图
MySQL 会根据以下规则来判断版本链中的哪个版本(记录)是在事务中可见的:
- trx_id < min_trx_id,那么该记录则在当前事务可见,因为修改该版本记录的事务在当前事务生成 Read View 之前就已经提交 。
- trx_id in (rw_trx_ids),那么该记录在当前事务不可见,因为需改该版本记录的事务在当前事务生成 Read View 之前还未提交 。
- trx_id > max_trx_id,那么该记录在当前事务不可见,因为修改该版本记录的事务在当前事务生成 Read View 之前还未开启 。
- trx_id = curr_trx_id,那么该记录在当前事务可见,因为修改该版本记录的事务就是当前事务 。
文章插图
我们首先步骤 1 中开启了一个读取事务,因为它是一个只读事务,所以它的事务 id 为 0(以下简称事务 0) 。紧接着我们在事务 0 中查询 id 为 1 的记录 。此时,版本链如下:
文章插图
注意:跟红色表头连接在一起的记录都是在 B+ 树中的,而通过 roll_ptr 指针连接的记录都是存在于 undo log 中的 。以下的所有版本链都是这种形式 。
推荐阅读
- 华为二面凉凉:Linux+Redis+MySQL+算法+网络+Java一个都讲不清
- 斗记茶业品牌历史,今年必看的斗记茶业漫画大事件
- 二 最详细MySQL事务隔离级别及原理讲解!
- 多向 异地多活-MySQL实时双向复制
- 储存茶叶必须注意哪些,茶叶的珍藏储存技巧小贴士
- 电脑操作者必备茶饮,上班族必知四款护眼茶
- |哪几个时辰出生的人,最近运气不给力,忍耐熬到最后,必时来运转
- 老人什么病必须手术
- 茶叶审评操作,5样必备茶叶审评用具
- MySQL 5.7 update误操作后数据恢复详解