首页 > 数据库 >Mysql 幻读

Mysql 幻读

时间:2023-07-21 20:12:50浏览次数:47  
标签:事务 快照 幻读 版本号 Mysql -- select

总结下:简单的事务操作,例如快照读,通过系统提供的mvcc机制——创建版本号、删除版
本号,避免幻读。而复杂的事务操作,例如当前度,则需要通过加锁进行控制。

参考一:幻读的基本概念

什么是幻读

事务不是独立执行造成的一种现象。一个事务对数据表进行了全面的修改,而后另一个事务增加了一行。这时候第一个事务中的另一个操作,发现表中有一个数据没有更新,以为产生了幻觉,这就是幻读。

共享锁和排它锁

贡献锁

共享用于不更改或不更新数据的操作,如select语句。如果不同的事务都是读操作,可以共享获取这把锁。

排它锁

用于数据修改操作,例如INSERT、UPDATE或DELETE。确保不会同时对同一资源进行多重更新。

当前读和快照读的区别

当前读

每次读取的都是当前最新的数据,但是读的时候不允许写,写的时候不允许读。基于共享锁+排它锁+Next-Key Lock实现。每次读取行时添加共享锁,读取范围时添加范围共享锁。每次修改行时添加排它锁,而范围行进行修改时,添加范围排它锁。保障每个事务读取的内容最新。

快照读

读写不冲突,每次读取的都是快照数据。这是基于MVCC+undolog实现的,可以保证读写不冲突,需要读取老版本的数据是,可以使用undolog中保存的内容。在Repeatable read(默认)下,有可能读取的不是最新的数据。但是在Read Committed隔离级别下,快照读和当前读读取的数据都一样,都是最新的。

mysql如何实现避免幻读

  • 在快照读的情况下,使用mysql使用mvcc来避免幻读
  • 在当前读的情况下,使用next-key来避免幻读

什么是MVCC,全称是多版本并发控制。mysql把每个操作定义为一个事务,每开启一个事务,系统的事务版本号自动递增。每行记录都有两个隐藏列:创建版本号和删除版本号。

  • select事务:事务每次只能读到创建版本号小于等于此次系统版本号的记录,同时删除版本号不存在或者大于系统版本号。
  • update事务:将当前系统版本号作为行记录的版本号,并将当前系统版本号作为原来行记录的删除版本号。
  • delete:将系统版本号作为行记录的删除版本号
  • insert:把当前系统版本号作为行记录的版本号。

什么是next-key锁(record locks+gap locks:行锁+间隙锁

什么是快照读和当前度:

  • 快照读:简单的select操作,属于快照读,不加锁。
    • select * from table where ?;
  • 当前读:特殊的读操作,插入/更新/删除操作,属于当前度,需要加锁。
    • select * from table where ? lock in share mode;
    • select * from table where ? for update;
    • insert into table values (…);
    • update table set ? where ?;
    • delete from table where ?;

参考二:RR事务级别下的幻读

事务的隔离级别概念

存在4中不同的事务隔离级别,不同的隔离级别下会产生脏读、幻读、不可重复读等相关问题。因此在选择事务的隔离级别的时候要根据应用场景来决定,使用合适的隔离界别。

在InnoDB存储引擎中提供了SQL标准

Read-uncommited 读未提交

存在脏读、幻读的问题。
支持行锁+表锁。

幻读:事务A执行期间,其他事物有写入操作,因为本级别没有快照,更没有MVCC机制,其他事务修改数据后即便不做提交,事务A使用select也会因为直接为当前读的模式,前后得到不同的结果导致幻读。

同时,其他事务最终可能会执行回滚操作,导致了事务A的脏读。

Read-commited 读已提交

存在幻读,

支持行锁 + 表锁。

在事务A执行期间,其他事务做一次数据插入并提交事务,事务A就会select出不同的结果导致幻读。在这种隔离界别下,快照读和当前读读取的内容是一样的。因为select每次都会去读取最新的快照数据。

而脏读问题被快照(ReadView)机制解决。

Repeatable-read 可重复读

可重复读存在幻读,但也可以避免幻读。

支持:行锁+间隙锁(gap)+ next-key lock(行锁+间隙锁)+ 表锁。

由于本级别支持的netx-key lock机制,才有了解决幻读的可能。行锁锁定等值记录,阻塞其他事务的即使读操作,间隙锁根据不同的查询条件,锁住与行相关的一个索引区间,阻塞其他事务对此索引区间做即使读操作(比如最直接引发幻读的insert操作)。

RR下幻读的场景模拟

在事务A中,先使用快照读,后使用当前读,导致事务A没有next-key lock锁定索引区间,其他事务对事务A所覆盖的查询范围中,元数据做了增删改,而后事务A又切换为当前读而非继续使用快照读,导致事务内前后查询到的数据集合不一致,出现了幻读。

逻辑如下所示:

-- 事务A
begin;
-- step1 快照读 不加锁 空结果集
select * from users where id >= 1;
-- step4 快照读 不会导致幻读 空结果集
select * from users where id >= 1;
-- step5 即时读 查询出事务B插入的数据 发生幻读
select * from users where id >= 1 for update;
-- step6
commit;

-- 事务B
begin;
-- step2 直接插入成功
insert into users value(1, "sqrt", 18, 1), (2, "cat", 19, 1);
-- step3
commit;

RR下杜绝幻读的方法:

不使用快照读,始终使用当前读,触发next-key lock锁机制,阻塞其他事务对当前事务查询条件所能覆盖的索引区间的数据当前读操作。(其实就相当于SERIALIZE级别了,该级别下没有快照读,会默认地给select也追加上lock in share mode的读锁)。

-- 事务A
begin;
-- step1 加上了间隙锁 [1, +无穷)
select * from users where id >= 1 for update;
-- step3 快照读/即时读 都不会出现幻读
select * from users where id >= 1;
select * from users where id >= 1 for update;
-- step4
commit;

-- 事务B
begin;
-- step2 会因为事务A step1 的间隙锁被阻塞
insert into users value(1, "sqrt", 18, 1), (2, "cat", 19, 1);
-- step5 事务A提交后 锁释放 才会执行插入
commit;

Serializable 串行化

序列化模式,解决了幻读的问题。

该模式下不存在快照读一说,因为所有的操作都做了锁机制,对某数据的访存操作转为了串行化。该模式下对select也隐式添加了lock in share mode的读锁。

支持行锁+间隙锁+next-key lock+表锁。

加锁实例

1、对主键的等值查询,数据不存在,会对相邻数据的主键区间加间隙锁。

若原表中的数据为[1, 2, 3, 5, 20],在查询主键为10的事务操作中,区间[5,20]会被锁定。

2、对主键等值查询,且数据存在,则为行锁,不影响其他事务对其他数据行的操作。

3、 若对主键范围查询,锁定范围区间。

总结

1、RR级别下的确存在幻读的可能
2、RR级别下通过合理的加锁,来启用next-key lock,即便是当前读,也可以防止幻读的
3、Serialize级别则是自动对select做lock in share mode的加锁模式,防止其他事务的当前读来修改数据,杜绝幻读。

其他参考

mysql当前读和快照读

标签:事务,快照,幻读,版本号,Mysql,--,select
From: https://www.cnblogs.com/ckxkexing/p/17572288.html

相关文章

  • 安装 MySQL ZIP 版本
    安装MySQL的ZIP版本相对于安装包版本稍微复杂一些,因为它需要手动解压和配置。以下是在Windows上安装MySQLZIP版本的步骤:下载MySQLZIP文件:前往MySQL官方网站(https://dev.mysql.com/downloads/mysql/)下载适合您Windows版本的MySQLZIP文件。选择与您系统......
  • java mysql连接串设置超时时间
    JavaMysql连接串设置超时时间在开发Java应用程序时,我们经常需要与数据库进行交互。而MySQL是一个常用的关系型数据库,因此在Java中连接MySQL数据库是一项常见的任务。在连接MySQL数据库时,有时候我们需要设置超时时间。超时时间是指在数据库连接过程中,如果连接的时间超过了指定的......
  • linux cpp g++ mysqlconnector
    1.installmysqlinubuntu;2installmysqlconnector;sudoaptinstalllibmysqlclient-dev3.completecode#include<algorithm>#include<chrono>#include<cstdio>#include<cstdint>#include<cstdlib>#include<ctime>......
  • MySQL(视图、事务、存储过程、函数、流程控制、索引)
    一视图(了解)什么是视图视图就是通过查询得到一张虚拟表,然后保存下来,下次可以直接使用为什么要用视图如果要频繁的操作一张虚拟表(拼表组成的),你就可以制作成视图,后续直接操作视图其实也是一张表如何操作#固定语法createview#具体操作createviewteacher2......
  • MySQL优化之group by
    MySQL之groupbygroupby优化 #删除掉目前的联合索引idx_user_pro_age_sta dropindexidx_user_ro_age_staontb_user; #执行分组操作,根据profession字段分组 explainselectprofession,count(*)fromtb_usergroupbyprofession; #创建索引 Createindexid......
  • MySQL之order by优化
    MySQL之orderby优化.Usingfilesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sortbuffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫FileSot排序。Usingindex:通过有序索引顺序扫描直接返回有序数据,这种情况即为usingindex,不需要......
  • MySQL之插入优化
    MySQL之插入优化insert优化原批量插入:insertintotb_testvalues(1,'tom');insertintotb_testvalues(2,'cat');insertintotb_testvalues(3,'jerry');改批量插入:insertintotb_testvalues(1,'Tom'),(2,'Cat'),(3,'Jer......
  • Mysql 命令行方式导出数据文件
    概述Linux服务器上有一个数据库表包含大于50亿条的记录,通过Navicat等数据迁移工具,将数据迁移到另一个服务器相同表中,总是执行一段时间后卡死,故选择先导出数据文件,再去另一个服务器导入该文件。可以使用Navicat导出数据文件,也可以使用MySQL支持的命令导出数据文件。本文章介绍如何使......
  • mysql id自增默认值
    MySQLID自增默认值在MySQL中,我们经常会使用自增的ID作为表的主键,以确保每个记录都有一个唯一的标识符。MySQL提供了一种方式来自动为这些ID生成唯一的值,即使用自增默认值。本文将介绍MySQL中的自增默认值的使用方法,并提供一些代码示例。什么是自增默认值自增默认值是一种特殊的......
  • mysql host 多个
    MySQL主机多个的使用方法MySQL是一种开源的关系型数据库管理系统,被广泛应用于各种Web应用程序中。在某些情况下,我们可能需要连接多个MySQL主机,这种情况下可以使用多个主机来提高应用程序的性能、可用性和可扩展性。本文将介绍如何在应用程序中使用多个MySQL主机。为什么使用多个M......