Mysql中mvcc各场景理解应用

Mysql中mvcc各场景理解应用

目录

前言

场景一

试验步骤

事务A第一步

事务B执行

事务A执行第二步

结果

场景二

试验步骤

事务A第一步

事务B执行

事务A执行第二步

结果

事务A后续步骤

场景三

场景四

事务A第一步

事务B执行

事务A第二步

事务A第三步

事务A第四步

原因

总结

前言

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场景的资料请关注易知道(ezd.cc)其它相关文章!

推荐阅读

    mac怎么安装mysql| macbook怎么下载mysql

    mac怎么安装mysql| macbook怎么下载mysql,标签,学习python 不得不学习的就是数据库,那么在开始学习数据库之前,那么今天我们先说下怎么安装m

    MySQL更新更新页面1/4。

    MySQL更新更新页面1/4。,,插入和替换 插入和替换语句的功能是将新数据插入表中,这两个语句的语法相似,它们之间的主要区别是如何处理重复的

    mysql数据库启动失败

    mysql数据库启动失败,报错,非正常,1、原因 公司服务器故障,非正常停机导致数据库启动失败。 报错信息 [ [email protected] dmp]# /etc/ini

    mysql长整型是什么

    mysql长整型是什么,整型,数据类型,语法,用户,填充,版本,MySQL长整型是“BIGINT”,是MySQL中最常用的数据类型之一,其可以用来存储较大的整数值,与

    深入理解MySQL分区

    深入理解MySQL分区,查询,数据,列表,索引,操作,按键,MySQL数据库是一个开源的关系型数据库管理系统。在一些大型数据环境中,为了更好地管理数据、

    mysql 如何查询

    mysql 如何查询,查询,数据,语句,条件,选取,排序,MySQL是一款常用的关系型数据库管理系统,被广泛应用于各种网站和应用开发。在MySQL中查询数据是

    MySQL中怎么实现分页操作

    MySQL中怎么实现分页操作,数据,显示,偏移量,分页,查询,初始,一、 背景什么是分页,就是查询时候数据量太大,一次性返回所有查询结果既耗费网络资源

    mysql怎么连接数据库

    mysql怎么连接数据库,服务,启动,数据库,命令,登录,步骤,mysql连接数据库的方法:1、通过计算机管理方式或通过命令行方式执行“net start mysql”

    mysql 查询拼接

    mysql 查询拼接,函数,字段,字符串,查询,分隔符,连接,MySQL 查询拼接在使用 MySQL 进行查询时,有时需要将多个字段或多张表中的字段进行拼接,这时

    怎么启动 mysql

    怎么启动 mysql,启动,服务器,输入,命令提示符,终端,命令,MySQL是一种广泛使用的关系型数据库管理系统。它是一种可扩展性强、性能卓越、跨平台

    mysql 如何卸载

    mysql 如何卸载,卸载,服务,命令,软件包,数据,安装,MySQL是一种广泛使用的数据库管理系统,用于管理数据的存储、检索和更新。在某些情况下,您需要

    mysql视图是什么

    mysql视图是什么,视图,查询,数据,替换,年龄,引用,mysql视图是一个虚拟表,其内容由查询定义;视图包含系列带有名称的列和行数据,而行和列数据来自定