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服务添加 iptables防火墙策略的方案
Apr 29 MySQL
Mysql效率优化定位较低sql的两种方式
May 26 MySQL
mysql5.7使用binlog 恢复数据的方法
Jun 03 MySQL
一篇文章带你深入了解Mysql触发器
Aug 02 MySQL
一文带你探究MySQL中的NULL
Nov 11 MySQL
mysql 联合索引生效的条件及索引失效的条件
Nov 20 MySQL
MySQL限制查询和数据排序介绍
Mar 25 MySQL
MySql分区类型及创建分区的方法
Apr 13 MySQL
MySQL 数据库范式化设计理论
Apr 22 MySQL
MySql数据库 查询时间序列间隔
May 11 MySQL
MySQL查询日期时间
May 15 MySQL
DQL数据查询语句使用示例
Dec 24 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
php的ajax简单实例
2014/02/27 PHP
php实现过滤表单提交中html标签的方法
2014/10/17 PHP
Yii2使用自带的UploadedFile实现的文件上传
2016/06/20 PHP
PHP与Java对比学习日期时间函数
2016/07/03 PHP
PHP 中 var_export、print_r、var_dump 调试中的区别
2018/06/19 PHP
Javascript load Page,load css,load js实现代码
2010/03/31 Javascript
js 静态动态成员 and 信息的封装和隐藏
2011/05/29 Javascript
JavaScript高级程序设计(第3版)学习笔记3 js简单数据类型
2012/10/11 Javascript
如何判断鼠标是否在DIV的区域内
2013/11/13 Javascript
VS2008中使用JavaScript调用WebServices
2014/12/18 Javascript
介绍JavaScript中Math.abs()方法的使用
2015/06/14 Javascript
弹出遮罩层后禁止滚动效果【实现代码】
2016/04/29 Javascript
js删除Array数组中指定元素的两种方法
2016/08/03 Javascript
概述jQuery中的ajax方法
2016/12/16 Javascript
Bootstrap php制作动态分页标签
2016/12/23 Javascript
jquery,js简单实现类似Angular.js双向绑定
2017/01/13 Javascript
提高Web性能的前端优化技巧总结
2017/02/27 Javascript
jQuery中的on与bind绑定事件区别实例详解
2017/02/28 Javascript
jQuery plugin animsition使用小结
2017/09/14 jQuery
微信小程序获取复选框全选反选选中的值(实例代码)
2019/12/17 Javascript
js实现列表按字母排序
2020/08/11 Javascript
vue自动添加浏览器兼容前后缀操作
2020/08/13 Javascript
[54:47]Liquid vs VP Supermajor决赛 BO 第五场 6.10
2018/07/05 DOTA
python基础教程之字典操作详解
2014/03/25 Python
用Python进行行为驱动开发的入门教程
2015/04/23 Python
使用Python制作获取网站目录的图形化程序
2015/05/04 Python
python 读写txt文件 json文件的实现方法
2016/10/22 Python
Python实现的端口扫描功能示例
2018/04/08 Python
python print输出延时,让其立刻输出的方法
2019/01/07 Python
Python 编程速成(推荐)
2019/04/15 Python
python ChainMap的使用和说明详解
2019/06/11 Python
Python调用OpenCV实现图像平滑代码实例
2020/06/19 Python
2014年政府采购工作总结
2014/12/09 职场文书
2015年设计师个人工作总结
2015/04/25 职场文书
nginx简单配置多个server的方法
2021/03/31 Servers
angular4实现带搜索的下拉框
2022/03/25 Javascript