MySQL事务隔离级别为"REPEATABLE-READ"下的"幻读"现象

MySQL事务隔离级别详解 全局事务要求在其中的所有参与的事务要么都提交,要么都回滚,这对于事务原有的 ACID 要求又有了提高。解决幻读的方式有很多,但是它们的核心思想就是一个事务在操作某张表数据的时候,另外一个事务不允许新增或者删除这张表中的数据了。从上面对 SQL 标准定义了四个隔离级别的介绍可以看出,标准的 SQL 隔离级别定义里,REPEATABLE-READ(可重复读)是不可以防止幻读的。还是刚才上面的读已提交的图,虽然避免了读未提交,但是却出现了,一个事务还没有结束,就发生了 不可重复读问题。并不会有任何性能损失。 阅读详情
我们用一个示例来讲解这种情况,及在并发事务进怎样避免这种情况。


CREATE TABLE `t1` (
  `a` int(11) NOT NULL,
  `b` int(11) DEFAULT NULL,
  PRIMARY KEY (`a`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;


session1                                                   session2
begin                                                      
select * from t1 where a > 50;                             
+----+-------+                                             
| a  | b     |                                             
+----+-------+                                             
| 51 |  3000 |                                             
| 52 | 10000 |                                             
| 53 |  5000 |                                             
| 54 |  3000 |                                             
+----+-------+                                             
4 rows in set (0.00 sec)                                   
                                                           
                                                           set autocommit = on;
                                                           insert into t1 values (55, 3000);
                                                           Query OK, 1 row affected (0.00 sec)
                                                           
select * from t1 where a > 50;                             
+----+-------+                                             
| a  | b     |                                             
+----+-------+                                             
| 51 |  3000 |                                             
| 52 | 10000 |                                             
| 53 |  5000 |                                             
| 54 |  3000 |                                             
+----+-------+                                             

4 rows in set (0.00 sec) 

#这里读不到55这条记录,这就是在"REPEATABLE-READ"级别下可以避免"不可重复读"的现象,如果是在"READ-COMMITTED"级别下是可以读到55这条记录的                                  

                                                           
update t1 set b = 3000 where a = 55;                       
Query OK, 0 rows affected (0.00 sec)                       
Rows matched: 1  Changed: 0  Warnings: 0                   
                                                           
select * from t1 where a > 50;                             
+----+-------+                                             
| a  | b     |                                             
+----+-------+                                             
| 51 |  3000 |                                             
| 52 | 10000 |                                             
| 53 |  3000 |                                             
| 54 |  3000 |                                             
| 55 |  3000 |                                             
+----+-------+                                             

5 rows in set (0.00 sec)    

#55这条记录被读到了,这就是"幻读现象",这只是多读取了一条记录,影响还不大,但是在下面的情况下影响就大了                                                                                          

commit;                                                    
                                                           
begin;                                                     
select * from t1 where a = 55;                             
+----+------+                                              
| a  | b    |                                              
+----+------+                                              
| 55 | 3000 |                                              
+----+------+                                              
1 row in set (0.00 sec)                                    
                                                           
                                                           update t1 set b = 8000 where a = 55;
                                                           Query OK, 1 row affected (0.00 sec)
                                                           Rows matched: 1  Changed: 1  Warnings: 0
                                                           
select * from t1 where a = 55;                             
+----+------+                                              
| a  | b    |                                              
+----+------+                                              
| 55 | 3000 |                                              
+----+------+                                              
1 row in set (0.00 sec)                                    
                                                           
update t1 set b = b + 3000 where a = 55;                   
Query OK, 1 row affected (0.00 sec)                        

Rows matched: 1  Changed: 1  Warnings: 0                  

                                                           
select * from t1 where a = 55;                             
+----+-------+                                             
| a  | b     |                                             
+----+-------+                                             
| 55 | 11000 |                                             
+----+-------+                                             
1 row in set (0.00 sec)                                    

#在这个事务中,理所当然认为b的值应该是3000+3000=6000,但是实际上是11000,"灵异幻读"现象出现了                                                    

commit;                                                    

                                                           

如果b是一个账户钱袋,在session1的事务开始后,sessino2的事务中还可以给钱袋加钱,这种情况必须避免,不然如果session1事务去更新钱袋,如:update t1 set b = 5000 where a= 55,那么session2事务中的8000就被冲掉了,"后果很严重"


下面看看怎样避免这种情况。


begin;                                                     
select * from t1 where a = 55 LOCK IN SHARE MODE; #加一把共享锁,这样另一事务不能修改这条记录
+----+-------+
| a  | b     |
+----+-------+
| 55 | 11000 |
+----+-------+
1 row in set (0.01 sec)


                                                           select * from t1 where a = 55;
                                                           +----+-------+
                                                           | a  | b     |
                                                           +----+-------+
                                                           | 55 | 11000 |
                                                           +----+-------+
                                                           1 row in set (0.00 sec)
                                                           
                                                           select * from t1 where a = 55 LOCK IN SHARE MODE;
                                                           +----+-------+
                                                           | a  | b     |
                                                           +----+-------+
                                                           | 55 | 11000 |
                                                           +----+-------+
                                                           1 row in set (0.00 sec)
                                                           #可以读
                                                           update t1 set b = b + 3000 where a = 55;
                                                           #wait ....但是不能修改
commit;                                                    
Query OK, 0 rows affected (0.00 sec)                       
                                                           
                                                           Query OK, 1 row affected (12.23 sec)
                                                           Rows matched: 1  Changed: 1  Warnings: 0
                                                           #直到事务1提交后,这个修改才被提交
select * from t1 where a = 55;                             
+----+-------+                                             
| a  | b     |                                             
+----+-------+                                             
| 55 | 14000 |                                             
+----+-------+                                             

1 row in set (0.00 sec)                                    


死锁由此产生:                                                           


begin;                                                     
select * from t1 where a = 55 LOCK IN SHARE MODE; #加一把共享锁,这样另一事务不能修改这条记录
+----+-------+
| a  | b     |
+----+-------+
| 55 | 11000 |
+----+-------+
1 row in set (0.01 sec)
                                                           
                                                           begin;
                                                           select * from t1 where a = 55 LOCK IN SHARE MODE; #也加一所共享锁
                                                           +----+-------+
                                                           | a  | b     |
                                                           +----+-------+
                                                           | 55 | 11000 |
                                                           +----+-------+
                                                           1 row in set (0.00 sec)
                                                           #可以读
                                                           update t1 set b = b + 3000 where a = 55;
                                                           #wait ....但是不能修改,因为会话一中在加锁

update t1 set b = b + 3000 where a = 55;
#ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction
                                                           
                                                           Query OK, 1 row affected (12.23 sec)
                                                           Rows matched: 1  Changed: 1  Warnings: 0
                                                           #直到事务死锁退出后,这个修改才被提交

所以在这种两个事务一开始都加SHARE LOCK的情况,由于不阻碍另一事件的读写,会导致后续的更新语句产生,可以加一互斥锁来避免,如下:


事务1开始后,事务2还能读到,也不太好,干脆让事务2读都读不出来 

                                                          

begin;                                                     
select * from t1 where a = 55 FOR UPDATE;                  
+----+-------+                                             
| a  | b     |                                             
+----+-------+                                             
| 55 | 14000 |                                             
+----+-------+                                             
1 row in set (0.00 sec)                                    
                                                           select * from t1 where a = 55;
                                                           +----+-------+
                                                           | a  | b     |
                                                           +----+-------+
                                                           | 55 | 14000 |
                                                           +----+-------+
                                                           1 row in set (29.70 sec)
                                                           #普通select语句还是可以读
                                                           select * from t1 where a = 55 LOCK IN SHARE MODE;
                                                           #wait ....但是加了共享锁的select语句读不到
                                                           
commit;                                                    
                                                           
                                                           +----+-------+
                                                           | a  | b     |
                                                           +----+-------+
                                                           | 55 | 14000 |
                                                           +----+-------+

                                                           1 row in set (29.70 sec)

                                                           #直到事务1提交后才能查询到结果


SELECT语句在InnoDB引擎默认下是不加锁的,一个事务即使FOR UPDATE锁定了一条记录,另一个事务中默认select语句还是可以读的,为了避免这种情况就要在select语句中显示的加锁LOCK IN SHARE MODE or FOR UPDATE,这样很麻烦对吧,那就将事务的隔离级别设置为"SERIALIZABLE","SERIALIZABLE"与"REPEATABLE-READ"的区别就是,前者在事务中所有的SELECT语句上都会默认的加上一个共享锁。

"SERIALIZABLE"实际上两个事务就完全串行了,"会影响并发性能",要结合具体业务来具体处理,大部分情况下不需要这么严格的。


浅析MySQL数据库在InnoDB存储引擎下的READ COMMITTED与REPEATABLE READ隔离级别以及不可重复读与幻读现象 一、InnoDB存储引擎下的一致性非锁定读与一致性锁定读 MySQL在InnoDB引擎下对于READ COMMITTED与REPEATABLE READ这两个隔离级别均采用的是一致性非锁定读,即所谓的多版本并发控制协议(MVCC),这样增加了并发性能。这两种隔离级别下默认的读操作都是读快照,但是读取的快照版本不一样,对于READ COMMITTED读取的是最新的快照版本,所以一个事务对数据的修改... 阅读详情

相关推荐

MYSQL的REPEATABLE-READ解决不可重复读和幻读

做了一个实验  create table t (id number, mount number); insert into t value(1,1);   A B 1 begin;   2 select * from t;   3   begin; 4   select * from t; 5 upd

爱米粒的博客 3332

MySQL事务隔离级别----Repeatable read(重复读)

根据实际需求,通过设置数据库的事务隔离级别可以解决多个事务并发情况下出现的脏读、不可重复读和幻读问题,数据库事务隔离级别由低到高依次为Read uncommitted、Read committed、Repeatable read和Serializable等四种。数据库不同,其支持的事务隔离级别亦不相同:MySQL数据库支持上面四种事务隔离级别,默认为Repeatable read;Oracle 数...

My_liuying0901 1万+

MySQL REPEATABLE READ隔离级别详解

隔离级别定义了一个事务在读取数据时如何与其他并发事务进行隔离。较低的隔离级别可以提高并发性能,但可能导致数据不一致性问题。而较高的隔离级别可以确保数据的一致性,但会降低并发性能。MySQL中定义了四个隔离级别,分别是READ UNCOMMITTED(读未提交)、READ COMMITTED(读已提交)、REPEATABLE READ(可重复读)和SERIALIZABLE(可串行化)。通过本文的介绍,我们详细了解了MySQL中的REPEATABLE READ隔离级别。

ThDebug的博客 747

MySQL数据库——-事务的隔离级别(四)Repeatable read

文章目录2、Read committed(读提交)脏读不可重复读幻读 2、Read committed(读提交) 可以避免脏读,但可能出现不可重复读和幻读。 设置MySQL的事务隔离级别为REPEATABLE-READ 在my.ini配置文件最后加上如下配置,然后在服务中重启MySQL服务[mysqld] transaction-isolation = REPEATABLE-READ 查...

paku-san的博客 1397

squidpy 教程 2:利用 Scanpy 和 Squidpy 进行空间数据的可视化分析

本教程详细介绍了如何利用Squidpy和Scanpy对空间转录组数据进行可视化分析。通过将Scanpy处理得到的聚类结果映射回原始组织图像,并结合基因表达与空间邻域信息,帮助研究者直观揭示细胞类型分布、基因表达模式及空间相互作用,从而深入挖掘空间组学数据的生物学意义。

weixin_29081703的博客 446

stc8g1k08硬件iic读取aht20温湿度及0.91寸oled显示

使用stc8g1k08读取aht20温湿度及0.91寸oled显示。

qq_61688416的博客 2770

mysql 事务 Repeatable Read

脏读:读到别的事务没有提交的数据。 不可重复读:先前读取的数据,被别的事务改变了,再读就跟原来不一样了。 幻读:一个事务插入了某条数据,另一个事务去读插入的那条数据的时候发现没有,更新这条记录的时候又成功了,再次查询这条记录就出现了。

lwpoor123的博客 1278

保姆级教程:Synology NAS共享文件夹从创建到权限设置的完整流程

本文提供了一份详尽的Synology NAS共享文件夹创建与权限设置指南。重点讲解了如何将NAS接入企业AD域,并利用域管理员账户进行集中身份验证和权限管理。文章从基础概念、域环境配置、文件夹创建、精细化权限分配到客户端访问与维护,完整覆盖了构建安全高效企业级文件共享空间的全流程,旨在帮助管理员实现便捷的团队协作与数据安全管理。

u9v0w的博客 552

mysql事务隔离级别幻读_MySQL事务隔离级别为"REPEATABLE-READ"下的"幻读"现象

MySQL事务隔离级别为"REPEATABLE-READ"下的"幻读"现象mysql中的可重复读这里打开两个mysql的命令行窗口,窗口A,即session1,窗口B,即session2。session1mysql>begin;QueryOK,0rowsaffected(0.00sec)mysql>select*fromt1;+----+------+|a...

weixin_33900596的博客 318

YOLOv11改进 - 卷积Conv | RFAConv:感受野注意力卷积动态调整感受野,提升小目标检出精度

本文介绍了感受野注意力卷积(RFAConv)及其在 YOLOv11 中的结合。空间注意力虽广泛应用但有局限,RFAConv 是将空间注意力与卷积操作融合的新型注意力机制,通过强化感受野空间特征关注、解决卷积核参数共享挑战、增强大尺寸卷积核处理能力等策略优化卷积核功能。它有基于 Group Conv 和 Unfold 两种实现方式,实验表明前者性能更好。我们将 RFAConv 集成进 YOLOv11,在数据集实验证明了方法优越性 。

魔改工程师的博客 1220

LPDDR6 Efficiency Mode 技术探讨

LPDDR6 Efficiency Mode 技术探讨

zilan23的博客 1156

Mysql在可重复读事务隔离级别下怎么解决幻读的

Mysql在可重复读(REPEATABLE READ)隔离级别下,如何解决部分幻读问题?本文将对这个问题进行探究。

7me 4201

Cadence画PCB的傻瓜式教程

1.原理图1)建立元件库2)建立原理图3)在原理图中画好电路图4)利用MENTOR-GRAPHICS-IPC-7351-LP-VIEWER查看元件的封装信息,用pad designer画焊盘,用pcb editor画元件的封装(对照LP_VIEWER),(也可以使用package wizard)5)在原理图中为元件添加封装信息(键入)6)Drc检查7)生成网表2.Pcb1)设置...

tianpu2320959696的博客 1万+

【MYSQL】事务隔离级别、脏读、不可重复读、幻读

(读取未提交) :最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。(读取已提交) :允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。(可重复读) :对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。(可串行化) :最高的隔离级别,完全服从 ACID 的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。

hhb442的博客 2871

Vue 网络处理 - axios 异步请求的使用,请求响应拦截器(最佳实践)

axios 用来在前端页面发起一个异步请求,请求之后页面不动,响应回来刷新局部.1.为什么不使用 ajax 呢?官方:在 jQuery 中推荐使用 ajax 技术, Vue 里面不推荐使用 jQuery 框架,因此 Vue 更推荐使用 axiox 异步请求库(axios并不是 vue 官方库).2.axios 特性可以从浏览器中创建 XMLHttpRequests可以从 node.js 中创建 http 请求.支持 Promise API.支持拦截请求和响应.转换请求数据和响应数据.

CYK_byte的博客 1万+

Modbus RTU 详解 + FreeMODBUS移植(附项目源码)

本文全面解析Modbus RTU通信协议,详细介绍其报文格式、功能码定义及典型应用场景,并手把手指导如何将FreeMODBUS协议栈移植到嵌入式平台。内容涵盖从底层硬件驱动配置、协议栈移植到主机/从机通信测试的全流程,重点讲解移植过程中的常见问题及解决方案。文章最后提供经过实测的完整项目源码,帮助开发者快速实现稳定的Modbus RTU通信功能。

Bit_Dong的博客 2926

事务隔离级别——REPEATABLE-READ(重复读)

首先,我们先设置MySQL事务隔离级别为REPEATABLE-READ 在my.ini配置文件最后加上如下配置 #可选参数有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE. [mysqld] transaction-isolation = REPEATABLE-READ 重启MySQL服务 1、脏读 提出问...

Zzze0101的博客 8266

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

MySQL-事务隔离级别:READ UNCOMMITTED(未提交读)、READ COMMITTED(已提交读)、REPEATABLE READ(可重复读)、SERIALIZABLE(可串行化)

1、事务隔离级别:READ UNCOMMITTED(未提交读)、READ COMMITTED(已提交读)、REPEATABLE READ(可重复读)、SERIALIZABLE(可串行化) 2、​ InnoDB存储引擎在MySQL数据库中负责处理事务和行级锁定。Next-Key Lock是InnoDB为实现REPEATABLE READ隔离级别而采用的一种锁定机制,它结合了Record Lock(记录锁)和Gap Lock(间隙锁),旨在解决幻读问题。

小孔靠得住的博客 4397

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

MySQL 的 Innodb 引擎在 REPEATABLE-READ(RR) 的事务隔离级别下,幻读问题记录

看过一些博客和文章,有的说 MySQL Innodb RR 级别下,解决了幻读问题,也有的说没有解决,也还有一些说解决了部分幻读问题,但是都没有想过实验去论证,据着严谨的我,特此实验一番记录一下 实验前提: 1.使用 SELECT @@tx_isolation 查询当前的事务隔离级别,确保是 REPEATABLE-READ 2.执行以下表结构语句创建表 CREATE TABLE `t_user` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `user.

weixin_55808466的博客 1万+

可重复读(Repeatable Read)能解决幻读吗?

MySQL的InnoDB引擎在默认的Repeatable Read隔离级别下,通过MVCC(多版本并发控制)和Next-Key Lock(记录锁+间隙锁)机制有效避免了幻读问题。MVCC确保普通查询基于事务开始时的快照读取,而Next-Key Lock则在对数据进行加锁操作时锁定记录和相邻间隙,防止其他事务插入新行。虽然SQL标准规定Repeatable Read不保证避免幻读,但InnoDB的这一实现使其在保持高性能的同时解决了幻读问题,通常无需使用性能更低的Serializable隔离级别。

笑衬人心的博客 1638

理解 MySQL 中的脏读、不可重复读、幻读及其事务隔离级别

选择合适的事务隔离级别对应用程序的性能和数据一致性至关重要。在设计数据库事务时,需要根据业务需求权衡不同类型的数据不一致问题,以及它们对系统性能的影响。通过理解和合理运用这些隔离级别,可以有效避免脏读、不可重复读和幻读,从而保障数据的正确性和系统的稳定性。

weixin_54574094的博客 1551

mysql事务隔离REPEATABLE-READ防止幻读问题

事务隔离级别: =========================================================================================== 隔离级别 脏读(Dirty Read) 不可重复读(NonRepeatable Read) 幻读(Phantom Read) =...

kaizhangzhang的专栏 2638

数据库事务隔离级别-- 脏读、幻读、不可重复读

一 数据库事务的隔离级别 数据库事务的隔离级别有4个,由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable,这四个级别可以逐个解决脏读 、不可重复读 、幻读这几类问题。 1. Read UnCommitted(读未提交) 最低的隔离级别。一个事务可以读取另一个事务并未提交的更新结果。 2. Read Committed(读提交) 大部分数据库采用的默认隔离级别。一个事务的更新操作结果只有在该事务提交之后,另一个事务才可

Roy_396的博客 6820

一篇文章读懂 MySQL 事务中的四种隔离级别,以及脏读、不可重复读、幻读详解

文章结合简单的案例演示 MySQL 事务中的四种隔离级别,以及脏读、不可重复读、幻读详解。

天怎么不会塌的博客 2987

”可重复读“隔离级别下MySQL是如何解决幻读问题的

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34378045的博客 6825

MySQL的InnoDB默认隔离级别为 Repeatable read(可重复读)为啥能解决幻读问题?

MySQL的InnoDB默认隔离级别为 Repeatable read(可重复读)为啥能解决幻读问题?

ratel的博客 2827
上一篇: Gearman分布式任务处理系统(十)后记(持续更新)
下一篇: 高性能MySQL.读书笔记(六)高可用性
笑天居士
博客等级 码龄24年 292粉丝 136原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值