Mysql中mvcc各场景理解应用


Posted in MySQL onAugust 05, 2022

前言

  • mysql版本为
mysql> select version();
+-----------+
| version() |
+-----------+
| 8.0.27    |
+-----------+
1 row in set (0.00 sec)
  • 隔离级别
mysql> show variables like '%isola%';
+-----------------------+-----------------+
| Variable_name         | Value           |
+-----------------------+-----------------+
| transaction_isolation | REPEATABLE-READ |
+-----------------------+-----------------+
1 row in set (0.02 sec)
  • 表结构
mysql> show create table test;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                                                                                                                                                                                 |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| test  | CREATE TABLE `test` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT '主键',
  `name` char(32) NOT NULL COMMENT '用户姓名',
  `num` int DEFAULT NULL,
  `phone` char(11) DEFAULT '' COMMENT '手机号',
  PRIMARY KEY (`id`),
  KEY `idx_name_phone` (`name`,`phone`)
) ENGINE=InnoDB AUTO_INCREMENT=108 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='test表'           |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec
  • 现有表数据
mysql> select * from test;
+-----+---------------+---------+-------+
| id  | name          | num     | phone |
+-----+---------------+---------+-------+
|   1 | 执行业        | 1234567 |       |
|   2 | 执行业务1     |    NULL |       |
|   3 | a             |    NULL |       |
|   4 | a             |    NULL |       |
|   5 | a             |    NULL |       |
|   6 | b             |       1 |       |
|   7 | wdf           |    NULL |       |
|  10 | dd            |       1 |       |
|  11 | hello         |    NULL |       |
|  15 | df            |    NULL |       |
|  16 | e             |    NULL |       |
|  20 | e             |    NULL |       |
|  21 | 好的          |    NULL |       |
|  25 | g             |       1 |       |
| 106 | hello         |    NULL |       |
| 107 | a             |    NULL |       |
+-----+---------------+---------+-------+
16 rows in set (0.00 sec)

场景一

  • 事务A:select * from test where id in (7,15) for update;
  • 事务B:update test set name='d' where id=10;insert into test(id,name) values(8,'hello');
  • 事务A:select * from test where id in (7,8,10,15);。 第二步是否阻塞。第三步是否能读到事务B执行的更新。

试验步骤

事务A第一步

mysql> begin;select * from test where id in (7,15) for update;
Query OK, 0 rows affected (0.00 sec)
+----+------+------+-------+
| id | name | num  | phone |
+----+------+------+-------+
|  7 | wdf  | NULL |       |
| 15 | df   | NULL |       |
+----+------+------+-------+
2 rows in set (0.01 sec)

持有锁情况:

mysql> select * from performance_schema.data_locks;
+--------+-----------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+-----------+
| ENGINE | ENGINE_LOCK_ID              | ENGINE_TRANSACTION_ID | THREAD_ID | EVENT_ID | OBJECT_SCHEMA | OBJECT_NAME | PARTITION_NAME | SUBPARTITION_NAME | INDEX_NAME | OBJECT_INSTANCE_BEGIN | LOCK_TYPE | LOCK_MODE     | LOCK_STATUS | LOCK_DATA |
+--------+-----------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+-----------+
| INNODB | 4974808984:1063:4890706744  |                 46666 |        50 |      123 | my_test       | test        | NULL           | NULL              | NULL       |            4890706744 | TABLE     | IX            | GRANTED     | NULL      |
| INNODB | 4974808984:2:4:7:4915866136 |                 46666 |        50 |      123 | my_test       | test        | NULL           | NULL              | PRIMARY    |            4915866136 | RECORD    | X,REC_NOT_GAP | GRANTED     | 15        |
| INNODB | 4974808984:2:4:9:4915866136 |                 46666 |        50 |      123 | my_test       | test        | NULL           | NULL              | PRIMARY    |            4915866136 | RECORD    | X,REC_NOT_GAP | GRANTED     | 7         |
+--------+-----------------------------+-----------------------+-----------+----------+---------------+-------------+----------------+-------------------+------------+-----------------------+-----------+---------------+-------------+-----------+
3 rows in set (0.00 sec)

发现7,15持有了行锁。

事务B执行

mysql> update test set name = 'sds' where id=10;insert into test(id,name) values(8,'hello');
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0
Query OK, 1 row affected (0.00 sec)

事务A执行第二步

mysql> select * from test where id in (7,8,10,15);
+----+-------+------+-------+
| id | name  | num  | phone |
+----+-------+------+-------+
|  7 | wdf   | NULL |       |
|  8 | hello | NULL |       |
| 10 | sds   |    1 |       |
| 15 | df    | NULL |       |
+----+-------+------+-------+
4 rows in set (0.01 sec)

结果

步骤二执行了,事务A读到了事务B提交的数据。下面我们来看看正常的select;

场景二

还原数据:

mysql> update test set name = 'dd' where id=10;delete from test where id=8;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1  Changed: 1  Warnings: 0
Query OK, 1 row affected (0.00 sec)
  • 事务A:select * from test where id in (7,15);
  • 事务B:update test set name='d' where id=10;insert into test(id,name) values(8,'hello');
  • 事务A:select * from test where id in (7,8,10,15);。 第二步是否阻塞。第三步是否能读到事务B执行的更新。

试验步骤

事务A第一步

mysql> begin;select * from test where id in (7,15);
Query OK, 0 rows affected (0.00 sec)
+----+------+------+-------+
| id | name | num  | phone |
+----+------+------+-------+
|  7 | wdf  | NULL |       |
| 15 | df   | NULL |       |
+----+------+------+-------+
2 rows in set (0.00 sec)

持有锁情况:

mysql> select * from performance_schema.data_locks;
Empty set (0.00 sec)

事务B执行

mysql> update test set name = 'sds' where id=10;insert into test(id,name) values(8,'hello');
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
Query OK, 1 row affected (0.00 sec)

事务A执行第二步

mysql> select * from test where id in (7,8,10,15);
+----+------+------+-------+
| id | name | num  | phone |
+----+------+------+-------+
|  7 | wdf  | NULL |       |
| 10 | dd   |    1 |       |
| 15 | df   | NULL |       |
+----+------+------+-------+
3 rows in set (0.00 sec)

结果

步骤二执行了,事务A没读到了事务B提交的数据。笔者猜测for update加锁之后会清除readview或者没开启readview,所以后面会读到事务B的。

所以我们来看看到底是清除还是没开启。

事务A后续步骤

mysql> select * from test where id in (7,15) for update;
+----+------+------+-------+
| id | name | num  | phone |
+----+------+------+-------+
|  7 | wdf  | NULL |       |
| 15 | df   | NULL |       |
+----+------+------+-------+
2 rows in set (0.00 sec)
mysql> select * from test where id in (7,8,10,15);
+----+------+------+-------+
| id | name | num  | phone |
+----+------+------+-------+
|  7 | wdf  | NULL |       |
| 10 | dd   |    1 |       |
| 15 | df   | NULL |       |
+----+------+------+-------+
3 rows in set (0.00 sec)

可以发现重新执行了场景一的步骤后结果没变。

所以应该是没开启,应该是当前读不会开启readview。

笔者找了下资料没找到,找到的笔者可以留言。

不过我们可以使用继续实验验证下。

场景三

  • 事务A:update test set name = 'dgf' where id in (7,15);
  • 事务B:update test set name='d' where id=10;insert into test(id,name) values(8,'hello');
  • 事务A:select * from test where id in (7,8,10,15);。 第二步是否阻塞。第三步是否能读到事务B执行的更新。

这个场景就不搞实验步骤了,结果是和笔者的猜想一样的 ”当前读不会开启readview,第一个快照读才会开启“

场景四

  • 事务A:select * from test where id in (7,15);
  • 事务B:insert into test(id,name) values(8,'hello');
  • 事务A:select * from test where id in (7,8,15);
  • 事务A:update test set name ='cv' where id =8;
  • 事务A:select * from test where id in (7,8,15);

事务A第一步

mysql> begin;select * from test where id in (7,15);
Query OK, 0 rows affected (0.00 sec)
+----+------+------+-------+
| id | name | num  | phone |
+----+------+------+-------+
|  7 | wdf  | NULL |       |
| 15 | df   | NULL |       |
+----+------+------+-------+
2 rows in set (0.00 sec)

开启了事务,浅读一下。

事务B执行

insert into test(id,name) values(8,'hello');

事务A第二步

mysql> select * from test where id in (7,8,15);
+----+------+------+-------+
| id | name | num  | phone |
+----+------+------+-------+
|  7 | wdf  | NULL |       |
| 15 | df   | NULL |       |
+----+------+------+-------+
2 rows in set (0.00 sec)

检验一下是否读的到,发现读不到。

事务A第三步

mysql> update test set name ='cv' where  id =8;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

对插入的进行更新。

事务A第四步

mysql> select * from test where id in (7,8,15);
+----+------+------+-------+
| id | name | num  | phone |
+----+------+------+-------+
|  7 | wdf  | NULL |       |
|  8 | cv   | NULL |       |
| 15 | df   | NULL |       |
+----+------+------+-------+
3 rows in set (0.00 sec)

发现可以读到了。

原因

能读到的原因是因为本事务对版本链内容进行了修改,所以就读到了。

这个场景可能会出现在实际开发中,会比较懵,当然“事务A第三步”是笔者随便模拟的,实际生产中直接拿大不到刚刚插入的id,所以应该是模糊(没有确定行)update。所以在生产中还是要确定行去进行修改,避免出现这种比较难理解的场景。

虽然也可以使用lock in share mode或者for update读当前借助next-key去实现不幻读(第二次读到第一次没有读到的行),还是需要根据具体业务选择。

总结

根据以上的场景,我们可以知道:

  • readview是第一个select的时候才会创建的。
  • rr级别下读快照如果中间出现修改版本链内容还是会出现幻读(很合理,但是不容易发现这个原因),如果真的要想做到不幻读还是要通过加锁(当然要有索引,没有的话就锁表了)。

以上就是Mysql中mvcc各场景理解的详细内容,更多关于Mysql mvcc场景的资料请关注三水点靠木其它相关文章!

MySQL 相关文章推荐
Mysql服务添加 iptables防火墙策略的方案
Apr 29 MySQL
mysql在项目中怎么选事务隔离级别
May 25 MySQL
简单了解 MySQL 中相关的锁
May 25 MySQL
MySQL 亿级数据导入导出及迁移笔记
Jun 18 MySQL
mysql主从复制的实现步骤
Oct 24 MySQL
MySQL Innodb索引机制详细介绍
Nov 23 MySQL
Pycharm远程调试和MySQL数据库授权问题
Mar 18 MySQL
MySQL插入数据与查询数据
Mar 25 MySQL
MySQL 执行数据库更新update操作的时候数据库卡死了
May 02 MySQL
手把手带你彻底卸载MySQL数据库
Jun 14 MySQL
mysql实现将字符串字段转为数字排序或比大小
Jun 14 MySQL
SQL语句中EXISTS的详细用法大全
Jun 25 MySQL
数据设计之权限的实现
一文解答什么是MySQL的回表
Aug 05 #MySQL
MySQL一劳永逸永久支持输入中文的方法实例
Aug 05 #MySQL
SQLServer常见数学函数梳理总结
Aug 05 #MySQL
MySQL生成千万测试数据以及遇到的问题
Aug 05 #MySQL
面试官问我Mysql的存储引擎了解多少
MySQL索引失效场景及解决方案
Jul 23 #MySQL
You might like
linux下删除7天前日志的代码(php+shell)
2011/01/02 PHP
Windows下安装PHP单元测试环境PHPUnit图文教程
2014/10/24 PHP
Thinkphp自定义代码生成工具及用法说明(附下载地址)
2016/05/27 PHP
PHP入门教程之使用Mysqli操作数据库的方法(连接,查询,事务回滚等)
2016/09/11 PHP
PHP微信分享开发详解
2017/01/14 PHP
Thinkphp极验滑动验证码实现步骤解析
2020/11/24 PHP
javascript Zifa FormValid 0.1表单验证 代码打包下载
2007/06/08 Javascript
asp.net网站开发中用jquery实现滚动浏览器滚动条加载数据(类似于腾讯微博)
2012/03/14 Javascript
Javascript代码在页面加载时的执行顺序介绍
2013/05/03 Javascript
ECMAScript中函数function类型
2015/06/03 Javascript
Bootstrap入门书籍之(一)排版
2016/02/17 Javascript
简易的JS计算器实现代码
2016/10/18 Javascript
详解angularJs中自定义directive的数据交互
2017/01/13 Javascript
Vue-cli@3.0 插件系统简析
2018/09/05 Javascript
JavaScript函数、闭包、原型、面向对象学习笔记
2018/09/06 Javascript
用Golang运行JavaScript的实现示例
2019/11/25 Javascript
JavaScript 函数用法详解【函数定义、参数、绑定、作用域、闭包等】
2020/05/12 Javascript
Element Card 卡片的具体使用
2020/07/26 Javascript
vue $mount 和 el的区别说明
2020/09/11 Javascript
vue-cli3 热更新配置操作
2020/09/18 Javascript
[40:17]2018DOTA2亚洲邀请赛 4.5 淘汰赛 LGD vs Liquid 第一场
2018/04/06 DOTA
Python Tkinter简单布局实例教程
2014/09/03 Python
Django学习笔记之Class-Based-View
2017/02/15 Python
python互斥锁、加锁、同步机制、异步通信知识总结
2018/02/11 Python
详解Python中的动态属性和特性
2018/04/07 Python
Python pymongo模块常用操作分析
2018/09/01 Python
详解将Django部署到Centos7全攻略
2018/09/26 Python
Python Django 封装分页成通用的模块详解
2019/08/21 Python
解决pycharm中导入自己写的.py函数出错问题
2020/02/12 Python
Python 多进程原理及实现
2020/12/21 Python
HTML块级标签汇总(小篇)
2016/07/13 HTML / CSS
雪花秀美国官方网站:韩国著名草本护肤化妆品品牌
2016/10/19 全球购物
同事去世追悼词
2015/06/23 职场文书
大学生就业指导课心得体会
2016/01/15 职场文书
Python入门之使用pandas分析excel数据
2021/05/12 Python
Python学习之os包使用教程详解
2022/03/21 Python