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中VARCHAR与CHAR格式数据的区别
May 26 MySQL
正确使用MySQL update语句
May 26 MySQL
浅谈MySQL next-key lock 加锁范围
Jun 07 MySQL
mysql备份策略的实现(全量备份+增量备份)
Jul 07 MySQL
SQL实现LeetCode(180.连续的数字)
Aug 04 MySQL
MySQL中几种插入和批量语句实例详解
Sep 14 MySQL
深入解析MySQL索引数据结构
Oct 16 MySQL
mysql中int(3)和int(10)的数值范围是否相同
Oct 16 MySQL
mysql的数据压缩性能对比详情
Nov 07 MySQL
一文弄懂MySQL中redo log与binlog的区别
Feb 15 MySQL
关于mysql中string和number的转换问题
Jun 14 MySQL
MySQL数据库实验之 触发器和存储过程
Jun 21 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
5款适合PHP使用的HTML编辑器推荐
2015/07/03 PHP
PHP的mysqli_select_db()函数讲解
2019/01/23 PHP
phpstudy隐藏index.php的方法
2020/09/21 PHP
JavaScript排序算法之希尔排序的2个实例
2014/04/04 Javascript
在Node.js中实现文件复制的方法和实例
2014/06/05 Javascript
js实现键盘控制DIV移动的方法
2015/01/10 Javascript
javascript去除空格方法小结
2015/05/21 Javascript
jQuery实现在最后一个元素之前插入新元素的方法
2015/07/18 Javascript
JavaScript 定时器 SetTimeout之定时刷新窗口和关闭窗口(代码超简单)
2016/02/26 Javascript
移动端翻页插件dropload.js(支持Zepto和jQuery)
2016/07/27 Javascript
BootStrap Fileinput的使用教程
2016/12/30 Javascript
微信小程序 template模板详解及实例代码
2017/03/09 Javascript
AngularJs每天学习之总体介绍
2017/08/07 Javascript
jQuery Ajax向服务端传递数组参数值的实例代码
2017/09/03 jQuery
深入理解nodejs搭建静态服务器(实现命令行)
2019/02/05 NodeJs
layui中的switch开关实现方法
2019/09/03 Javascript
[02:38]2018年度DOTA2最佳劣单位选手-完美盛典
2018/12/17 DOTA
分析并输出Python代码依赖的库的实现代码
2015/08/09 Python
mac安装pytorch及系统的numpy更新方法
2018/07/26 Python
Python实现输入二叉树的先序和中序遍历,再输出后序遍历操作示例
2018/07/27 Python
Python绘制KS曲线的实现方法
2018/08/13 Python
在python中实现调用可执行文件.exe的3种方法
2019/07/07 Python
python类中super() 的使用解析
2019/12/19 Python
Python网络爬虫信息提取mooc代码实例
2020/03/06 Python
Python爬虫设置ip代理过程解析
2020/07/20 Python
微信浏览器取消缓存的方法
2015/03/28 HTML / CSS
Ruby如何创建一个线程
2013/03/10 面试题
学校课外活动总结
2014/05/08 职场文书
反对四风问题自我剖析材料
2014/09/29 职场文书
2014年仓库管理员工作总结
2014/11/18 职场文书
事业单位财务人员岗位职责
2015/04/14 职场文书
毕业论文致谢部分怎么写
2015/05/14 职场文书
贷款收入证明格式
2015/06/24 职场文书
吧主申请感言怎么写
2015/08/03 职场文书
初一英语教学反思
2016/02/15 职场文书
一篇文章弄懂Python关键字、标识符和变量
2021/07/15 Python