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的版本
May 19 MySQL
MySQL 全文索引使用指南
May 25 MySQL
带你学习MySQL执行计划
May 31 MySQL
MySQL中的布尔值,怎么存储false或true
Jun 04 MySQL
mysql优化之query_cache_limit参数说明
Jul 01 MySQL
一篇文章带你深入了解Mysql触发器
Aug 02 MySQL
MySQL基础快速入门知识总结(附思维导图)
Sep 25 MySQL
mysql5.7的安装及Navicate长久免费使用的实现过程
Nov 17 MySQL
Mysql 数据库中的 redo log 和 binlog 写入策略
Apr 26 MySQL
MySQL 逻辑备份 into outfile
May 15 MySQL
MySQL导致索引失效的几种情况
Jun 25 MySQL
SQL Server数据库的三种创建方法汇总
May 08 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实现分页的一个示例
2006/10/09 PHP
php下使用iconv需要注意的问题
2010/11/20 PHP
Codeigniter购物车类不能添加中文的解决方法
2014/11/29 PHP
php的debug相关函数用法示例
2016/07/11 PHP
详解PHP多个进程配合redis的有序集合实现大文件去重
2019/03/06 PHP
js中document.getElementByid、document.all和document.layers区分介绍
2011/12/08 Javascript
js鼠标滑过图片震动特效的方法
2015/02/17 Javascript
a标签置灰不可点击的实现方法
2017/02/06 Javascript
vue-resource调用promise取数据方式详解
2017/07/21 Javascript
javascript用defineProperty实现简单的双向绑定方法
2020/04/03 Javascript
详解vue-router的导航钩子(导航守卫)
2020/11/02 Javascript
[01:15:44]首部DOTA2纪录片今日23时全网上映
2014/03/19 DOTA
Python RuntimeError: thread.__init__() not called解决方法
2015/04/28 Python
Python自动化测试ConfigParser模块读写配置文件
2016/08/15 Python
Python语言生成水仙花数代码示例
2017/12/18 Python
python最长回文串算法
2018/06/04 Python
Python实现繁?转为简体的方法示例
2018/12/18 Python
python异步实现定时任务和周期任务的方法
2019/06/29 Python
详解Python实现进度条的4种方式
2020/01/15 Python
tensorflow实现将ckpt转pb文件的方法
2020/04/22 Python
Django静态资源部署404问题解决方案
2020/05/11 Python
Python基于traceback模块获取异常信息
2020/07/23 Python
CSS3贝塞尔曲线示例:创建链接悬停动画效果
2020/11/19 HTML / CSS
Jeep牧马人、切诺基和自由人零配件:4 Wheel Drive Hardware
2017/07/02 全球购物
西班牙英格列斯百货法国官网:El Corte Inglés法国
2017/07/09 全球购物
新加坡交友网站:be2新加坡
2019/04/10 全球购物
sort命令的作用和用法
2012/11/04 面试题
餐厅经理岗位职责和岗位目标
2014/02/13 职场文书
优秀教师演讲稿
2014/05/06 职场文书
大学生社会实践方案
2014/05/11 职场文书
本科生就业推荐信
2014/05/19 职场文书
2015幼儿园庆元旦活动方案
2014/12/09 职场文书
2015年安全员工作总结范文
2015/04/22 职场文书
学校教代会开幕词
2016/03/04 职场文书
高一作文之暖冬
2019/11/09 职场文书
Python基础之教你怎么在M1系统上使用pandas
2021/05/08 Python