MySQL数据库Innodb 引擎实现mvcc锁


Posted in MySQL onMay 06, 2022

前言:

大家都知道在java 开发过程中,会经常用到锁,在java 代码中,我们都知道锁是加在对象头上的,在java对象布局中有锁的标志位。程序通过判断锁的标志位来获取加锁的情况。但是在mysql 中,锁的实现原理是什么呢。可能大家都听过 mvcc,但是mvcc 的实现原理是什么呢,可能就说不太清楚了,本文就以实例说明来mvcc 的实现原理。

1 数据库设置隔离级别

我们都知道数据库的隔离级别可以分为以下:

  • 读未提交 RU : read uncommitted  
  • 读提交   RC : read committed 
  • 可重复读 RR : repeatable read 默认隔离级别
  • 序列化   SE : serializable 序列化

我们使用select @@transaction_isolation;来查看数据库的隔离级别,可能有读者会有疑问了,不是应该是select @@tx_isolation;吗, 因为我的电脑上装的是 mysql 8.0, 之前的 tx 也是 transaction的简写,高版本的mysql 已经使用 transaction 了。

# 查询当前会话隔离级别
select @@transaction_isolation;
# 查看当前系统的隔离级别
select @@global.transaction_isolation;
# 命令行开启事务,区别在于前者是第一条语句的执行时间点为事务开始的时间点,建立一致性读,后者是立即建立一致性读,开始执行事务
start transaction 或者 start transaction with consistent snapshot
# 设置数据库隔离级别,如若设置会话级别,则修改 global 为session 即可
set global transaction isolation level REPEATABLE READ;
set global transaction isolation level READ COMMITTED;
set global transaction isolation level READ UNCOMMITTED;
set global transaction isolation level SERIALIZABLE;

2 数据库表以及案例操作

操作的数据库表为:

CREATE TABLE `t_user` (
  `id` int NOT NULL AUTO_INCREMENT COMMENT '主键',
  `age` int DEFAULT NULL COMMENT '年龄',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
# 初始化语句
INSERT INTO `t_user`(`id`, `age`) VALUES (1, 3);
# 使用的操作语句
select * from t_user where id = 1;
update t_user set age = age + 1  where id = 1;

案例如图所示:

MySQL数据库Innodb 引擎实现mvcc锁

 sessionA,sessionB,sessionC 是连接数据库的3个会话窗口,mysql 数据库的默认隔离级别为RR,为了达到效果我们把当前的数据库隔离级别改为RC,语句如下:

set session transaction isolation level READ COMMITTED;

setp1 准备工作,设置当前会话事务隔离级别,查看数据库表中的数据。窗口从左往右依次是sessionA、sessinB和sessionC。 

MySQL数据库Innodb 引擎实现mvcc锁

setp2 t1 时刻,sessionA 和 sessionB 开启事务。sessionA t2 时刻执行查询语句,sessionC 执行更新操作,可以看到此时 sessionC 立马执行成功。 

MySQL数据库Innodb 引擎实现mvcc锁

step3 t3 时刻,sessionB 查询最新的数据,并执行更新操作 

MySQL数据库Innodb 引擎实现mvcc锁

step4 t4 时刻,sessionA 查询最新数据,并在t5 时刻更新数据,这里因为sessionB 的事务还未结束,因此执行的操作会阻塞到超时(这里刻意等待到超时),t6 时刻sessionB 查询数据,并提交事务。 

MySQL数据库Innodb 引擎实现mvcc锁

step5 sesionB 提交事务后,sessionA 重新执行语句,可以看到执行后的结果,然后提交事务。 

MySQL数据库Innodb 引擎实现mvcc锁

通过上述的操作案例,我们可以观察到:

  • 在一个事务中的数据更改对本事务是可见的,此外的事务是否可见取决于其隔离级别。
  • 两个事务操作同一条数据,会造成等待。innodb 引擎默认开启死锁检测,并会设置锁的超时时间。
# 设置超时时间默认是50s
innodb_lock_wait_timeout 
# 开启死锁检测,默认是开启死锁检测,默认值为 on,开启死锁检测
innodb_deadlock_detect
# 查看更多innodb 引擎的配置
show variables like '%innodb%'
  • 在会话中进行数据修改,默认事务是自动提交的。

3 mvcc 实现原理

mvcc,Multi-Version Concurrency Control,即版本并发访问控制,是 mysql innodb 引擎实现的一种并发访问控制方法,用于其事务的实现。其主要作用是为了提高数据库的并发性能,更好地处理读写冲突问题,在遇到冲突的境况也能够做到不加锁,非阻塞并发读取数据。 在解释实现原理前,先了解一下当前读和快照读。

  • 当前读:像更改数据的操作(update/delete/insert)操作还有共享锁和排他锁(lock in share mode和for update)这操作都需要读取当前最新版本的数据,否则就会有更新丢失的情况。数据库的修改操作也是将数据所在的数据页从磁盘加载到内存,然后进行修改,最后写回到磁盘中。
  • 快照读:不加锁的select获取数据就是读快照,不会产生阻塞。在串行化的数据库隔离级别下,快照读会退化成当前度。每行数据都有多个版本,当前读就是获取最新的已经提交过得版本数据。

接下来讲述的是非常重要的部分:

  • 已经提交的事务
  • 未被提交的事务
  • 未开始的事务

在可重复读的隔离级别下,事务启动需要对整个库做备份,这显然是不可能的,实际上mvcc也没有这样做,而是利用版本号来控制,也就是事务开始时向 innodb 事务系统申请一个 transaction id,这个id申请顺序是严格递增的。每行数据的版本号 row trx_id = transaction id , 也就可以保证其版本的唯一性。可以保证事务启动时,会获取当前所有活跃事务id的数组,可以保证其唯一性。事务id数组中的最小的id记为低水位,数组中的最大id+1记为高水位。低水位和高水位便是已经提交的事务-未提交的数据、未提交的事务-未开始的事务的分界标志位,以上就组成了当前事务的一致性视图(read-view)。

前面已经提到了,所有的数据都是有版本的,这个版本即row trx_id。当需要读快照时,就需要读取trx_id小于低水位且最大的trx_id版本号的数据。读当前就是读取当前最新版本的数据,如果数据正在事务的处理中,那么久需要等待,这也是为什么会出现锁等待超时的情况。

当在RC的数据库隔离级别下,每次的操作都会重新获取当前活跃的trx_id数组,形成新的一致性视图,这就是sessionB 在t3 时刻读取到了sessionC 在t2时刻提交的数据,因为在新的一致性视图中,sessionC 的trx_id 已经已经提交的事务了,所以就可以读取到。在sessionA 的t5 时刻,拿到的一致性视图中活跃的trx_id 数组中包含 sessionB 的活跃id,因为在更新数据时,需要当前读,而数据已经被锁住,所以出现了锁超时的情况。

而在RR的数据库隔离级别下,在事务开始时生成一个一致性视图,此后在事务提交之前,其 trx_id 数组不会发生变化,这样才能保证其可重复读的特性。RR相对于RC实现简单,区别在于是否在执行语句前更新一致性视图,活跃事务id的数组是否更新。

综上,我们就知道了mvcc 是如何实现可重复读和读提交的隔离级别了。

4 ACID 的实现

  • 事务的原子性 A 是通过 undolog 回滚日志来实现。
  • 事务的持久性 C 性是通过 redolog 重做日志来实现的。
  • 事务的隔离性 I 是通过 MVCC 来实现的。
  • 原子性,持久性,隔离性都实现了,那么一致性 D 也就实现了。

redo log 是mysql innodb 引擎产生的物理日志,其大小有一定的限制,采用循环写入的方式,写满之后进行刷盘。主要用于宕机后的数据恢复。redo log是一个循环写入的日志,可以理解为一个环,有 checkpoint 和 write pos 两个标志点,checkpoint之前的空间是清除后的可写空间,清除之前会更新到磁盘中,write pos是数据写入的位置,当两个标志点相遇表明redo log已经满了,这时数据库停止进行数据库更新语句的执行,转而进行redo log日志同步到磁盘中。

binlog 是mysql server 层记录逻辑的日志,可以一致追加写入,主要用于主从数据同步。

到此这篇关于MYSQL数据库Innodb 引擎mvcc锁实现原理的文章就介绍到这了!


Tags in this post...

MySQL 相关文章推荐
mysql知识点整理
Apr 05 MySQL
MySQL安装后默认自带数据库的作用详解
Apr 27 MySQL
JDBC连接的六步实例代码(与mysql连接)
May 12 MySQL
分析MySQL抛出异常的几种常见解决方式
May 18 MySQL
Mysql基础知识点汇总
May 26 MySQL
Navicat for MySQL的使用教程详解
May 27 MySQL
一篇文章带你深入了解Mysql触发器
Aug 02 MySQL
MySQL利用UNION连接2个查询排序失效详解
Nov 20 MySQL
分享MySQL常用 内核 Debug 几种常见方法
Mar 17 MySQL
mysql使用instr达到in(字符串)的效果
Apr 03 MySQL
MySQL创建管理LIST分区
Apr 13 MySQL
MYSQL常用函数介绍
May 05 MySQL
讲解MySQL增删改操作
May 06 #MySQL
解决Mysql报错 Table 'mysql.user' doesn't exist
MYSQL常用函数介绍
May 05 #MySQL
MySQL 数据 data 基本操作
May 04 #MySQL
MySQL 字符集 character
May 04 #MySQL
MySQL 数据表操作
May 04 #MySQL
MySQL 执行数据库更新update操作的时候数据库卡死了
May 02 #MySQL
You might like
Erlang的运算符(比较运算符,数值运算符,移位运算符,逻辑运算符)
2012/07/23 PHP
一个简单安全的PHP验证码类、PHP验证码
2016/09/24 PHP
Jquery css函数用法(判断标签是否拥有某属性)
2011/05/28 Javascript
javascript基础之查找元素的详细介绍(访问节点)
2013/07/05 Javascript
js 判断上传文件大小及格式代码
2013/11/13 Javascript
node.js入门教程
2014/06/01 Javascript
浅谈jquery回调函数callback的使用
2015/01/30 Javascript
JavaScript中使用Object.prototype.toString判断是否为数组
2015/04/01 Javascript
innerHTML中标签可以换行的方法汇总
2015/08/14 Javascript
JS使用cookie实现DIV提示框只显示一次的方法
2015/11/05 Javascript
js实现简单的碰壁反弹效果
2016/08/30 Javascript
JQuery和HTML5 Canvas实现弹幕效果
2017/01/04 Javascript
关于Angular2 + node接口调试的解决方案
2017/05/28 Javascript
深入解析koa之中间件流程控制
2019/06/17 Javascript
JavaScript剩余操作符Rest Operator详解
2019/07/20 Javascript
Vue实现穿梭框效果
2020/09/30 Javascript
[00:52]DOTA2齐天大圣预告片
2016/08/13 DOTA
python获取一组数据里最大值max函数用法实例
2015/05/26 Python
Python制作简单的网页爬虫
2015/11/22 Python
解决Python出现_warn_unsafe_extraction问题的方法
2016/03/24 Python
python中subprocess批量执行linux命令
2018/04/27 Python
python调用百度语音识别api
2018/08/30 Python
pyinstaller打包单个exe后无法执行错误的解决方法
2019/06/21 Python
华为2019校招笔试题之处理字符串(python版)
2019/06/25 Python
python lambda表达式(匿名函数)写法解析
2019/09/16 Python
python 实现任务管理清单案例
2020/04/25 Python
解决更改AUTH_USER_MODEL后出现的问题
2020/05/14 Python
python实现npy格式文件转换为txt文件操作
2020/07/01 Python
three.js模拟实现太阳系行星体系功能
2019/09/03 HTML / CSS
英国户外装备和冒险服装零售商:alloutdoor
2018/01/30 全球购物
数控专业个人求职信范例
2013/11/29 职场文书
会计专业自荐信范文
2013/12/02 职场文书
个人事迹材料范文
2014/12/29 职场文书
向雷锋同志学习倡议书
2015/04/27 职场文书
2015年社区国庆节活动总结
2015/07/30 职场文书
公司仓库管理制度
2015/08/04 职场文书