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 22 MySQL
MySQL 自定义变量的概念及特点
May 13 MySQL
Mysql数据库命令大全
May 26 MySQL
如何自己动手写SQL执行引擎
Jun 02 MySQL
MySQL 如何设计统计数据表
Jun 15 MySQL
解决Mysql的left join无效及使用的注意事项说明
Jul 01 MySQL
mysql备份策略的实现(全量备份+增量备份)
Jul 07 MySQL
MySQL Shell import_table数据导入的实现
Aug 07 MySQL
MyBatis 动态SQL全面详解
Oct 05 MySQL
MySQL分区表实现按月份归类
Nov 01 MySQL
mysql中varchar类型的日期进行比较、排序等操作的实现
Nov 17 MySQL
delete in子查询不走索引问题分析
Jul 07 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 杂谈《重构-改善既有代码的设计》之一 重新组织你的函数
2012/04/09 PHP
php使用ZipArchive提示Fatal error: Class ZipArchive not found in的解决方法
2014/11/04 PHP
Javascript 设计模式(二) 闭包
2010/05/26 Javascript
HTML上传控件取消选择
2013/03/06 Javascript
计算新浪Weibo消息长度(还可以输入119字)
2013/07/02 Javascript
js中实现字符串和数组的相互转化详解
2016/01/24 Javascript
JavaScript实现数据类型的相互转换
2016/03/06 Javascript
ichart.js绘制虚线、平均分虚线效果的实现代码
2016/05/05 Javascript
JavaScript生成带有缩进的表格代码
2016/06/15 Javascript
浅析Javascript ES6新增值比较函数Object.is
2016/08/24 Javascript
jQuery实现的自定义滚动条实例详解
2016/09/20 Javascript
JavaScript实现窗口抖动效果
2016/10/19 Javascript
微信小程序 textarea 详解及简单使用方法
2016/12/05 Javascript
Vue项目中quill-editor带样式编辑器的使用方法
2017/08/08 Javascript
Vue2.0+ElementUI实现表格翻页的实例
2017/10/23 Javascript
vue.js实现点击后动态添加class及删除同级class的实现代码
2018/04/04 Javascript
JS数组方法push()、pop()用法实例分析
2020/01/18 Javascript
vue-cli3使用mock数据的方法分析
2020/03/16 Javascript
JavaScript中clientWidth,offsetWidth,scrollWidth的区别
2021/01/25 Javascript
下载糗事百科的内容_python版
2008/12/07 Python
简单实现python爬虫功能
2015/12/31 Python
Python常见字典内建函数用法示例
2018/05/14 Python
Python使用eval函数执行动态标表达式过程详解
2020/10/17 Python
python 爬虫基本使用——统计杭电oj题目正确率并排序
2020/10/26 Python
纯CSS3实现滚动的齿轮动画效果
2014/06/05 HTML / CSS
phpquery中文手册
2021/03/18 PHP
美国时尚在线:Showpo
2017/09/08 全球购物
英国最大的宝石首饰超市:QP Jewellers
2018/09/23 全球购物
工艺工程师岗位职责
2014/03/04 职场文书
班级文化建设标语
2014/06/23 职场文书
建筑学专业自荐书
2014/07/09 职场文书
小学标准化建设汇报材料
2014/08/16 职场文书
个人贷款授权委托书样本
2014/10/07 职场文书
办理护照工作证明
2014/10/10 职场文书
分居协议书范本
2014/11/03 职场文书
四大名著读书笔记
2015/06/25 职场文书