4

MySQL幻读及解决方法

 3 years ago
source link: https://zhuanlan.zhihu.com/p/360254683
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

MySQL幻读及解决方法

工程师,期望当一个坐台歌手,个人主页:www.d-kai.me

这是一篇数据库隔离级别的科普文章,旨在了解数据库中著名的幻读现象,为了专注,对脏读、不可重复读不作讨论。

事务隔离级别

MySQL有四级事务隔离级别:

读未提交 READ-UNCOMMITTED: 存在脏读,不可重复读,幻读的问题
读已提交 READ-COMMITTED:不存在脏读,但存在不可重复读,幻读问题
可重复读 REPEATABLE-READ:不存在脏读,不可重复读问题,但存在幻读问题
序列化SERIALIZABLE:解决脏读,不可重复读,幻读问题,但完全串行执行,性能最低

什么是幻读

幻读错误的理解:说幻读是事务A 执行两次 select 操作得到不同的数据集,即 select 1 得到10条记录,select 2 得到11条记录。这其实并不是幻读,这是不可重复读的一种,只会在 R-U R-C 级别下出现,而在 mysql 默认的 RR 隔离级别是不会出现的。

这里给出我对幻读的理解:

幻读,并不是说事务中多次读取获取的结果集不同,幻读更重要的是某次的 select 操作得到的结果集所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 记录不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,如同产生了幻觉

举个例子可能会简化理解:

mysql> show create table user\G
*************************** 1. row ***************************
       Table: user
Create Table: CREATE TABLE `user` (
  `id` int(11) NOT NULL,
  `name` varchar(32) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

分别开启两个事务T1 & T2,并设置其隔离级别为Reaptable-Read:

mysql> set global transaction isolation level repeatable read;                                                                                 
​
mysql> begin;
mysql> select * from user;
mysql> insert into user values (1, 'jeff');
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
​
mysql> select * from user;
mysql> set global transaction isolation level repeatable read;                                                                                 
​
mysql> begin;
mysql> insert into user values (1, 'jeff');
mysql> commit;

T1 事务检测表中是否有 id 为 1 的记录,没有则插入

T2 插入干扰记录,造成T1出现幻读。

上例中需要确保T1事务执行begin后才开始执行事务T2。

上例中T1就发生了幻读,因为 T1读取的数据状态与后面的动作发生了语义上的冲突:查询的时候明明提示记录不存在,插入的时候去提示主键重复,类似于出现幻影,因而称之为幻读。

如何消除幻读

MySQL当前有两种方式可以消除幻读:

1. 通过对select操作手动加行X锁(SELECT ... FOR UPDATE )。原因是InnoDB中行锁锁定的
是索引,纵然当前记录不存在,当前事务也会获得一把记录锁(记录存在就加行X锁,不
存在就加next-key lock间隙X锁),这样其他事务则无法插入此索引的记录,杜绝幻
读。
2. 进一步提升隔离级别为SERIALIZABLE

测试一下效果

mysql> begin;
​
mysql> select * from user where id = 2 for update;
mysql> insert into user values (2, 'tony');

mysql> commit;
mysql> begin;
​
mysql> insert into user values (2, 'jimmy');
ERROR 1062 (23000): Duplicate entry '2' for key 'PRIMARY'

现在T1查询时携带了for update,在Innodb内会对该索引加锁(即使当前不存在),于是事务T2的insert会被阻塞直到T1显示提交,这样T1成功了,对于T1来说,幻读确实被消除了,但T2的插入会报主键重复,这也符合预期。

至于另外一种提升隔离级别消除幻读的方式感兴趣的可以自己尝试,这里不再重复,其本质是类似的,只是让系统代替了手工加锁。

总结

RR作为 mysql 事务默认隔离级别,是事务安全与性能的折中,正确认识幻读后,开发者便可以根据需求自行决定是否需要防止幻读。

SERIALIZABLE则是悲观的认为幻读时刻都会发生,故会自动的隐式的对事务所需资源加排它锁,其他事务访问此资源会被阻塞等待,故事务是安全的,但需要认真考虑性能。

InnoDB的锁是针对索引,这点需要引起注意。对行记录加锁,如果存在,加X锁,否则会加 next-key lock / gap 锁 / 间隙锁,故InnoDB可以实现事务对某记录的预先占用,只要本事务还在,其他事务就别想占有它。关于锁,后面还会再有专门的文章讨论。


Recommend

  • 54
    • 微信 mp.weixin.qq.com 5 years ago
    • Cache

    你真的明白什么是幻读吗?

  • 45
    • codesky.me 5 years ago
    • Cache

    脏读、幻读与不可重复读

    最近在读 《MySQL 技术内幕 InnoDB 存储引擎》,里面提到的各种概念都很新鲜,以前听说过脏读、幻读、不可重复读,但是对于概念不甚了解,于是查了一下,这里做个笔记。 数据库事务特征 数据库事务特征,即 ACID:

  • 33
    • www.tuicool.com 4 years ago
    • Cache

    面试官:什么是脏读、幻读?

    一、事务 事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。--摘自百科 在MySQL里,事务是在引擎层面实现,比如MyIsam不支持,InnoDB支持 二、ACID 提到事务,...

  • 3

    MySQL InnoDB引擎在Repeatable Read(可重复读)隔离级别下,到底有没有解决幻读的问题? 网上众说纷纭,有的说解决了,有的说没解决,甚至有些大v的意见都无法达成统一。 今天就深入剖析一下,彻底解决这个幻读的问题。 解决幻读问题之前,先...

  • 2

    美团三面:一直追问我, MySQL 幻读被彻底解决了吗? 作者:小林coding 2022-09-21 09:00:10 MySQL 可重复读隔离级别并没有彻底解决幻读,只是很大程度上避免了幻读现象的发生。 大家好,我是小林。

  • 2
    • www.cnblogs.com 1 year ago
    • Cache

    InnoDB 是如何解决幻读的 - mghio

    大部分人在日常的业务开发中,其实很少去关注数据库的事务相关问题,基本上都是 CURD 一把梭。正好最近在看 MySQL 的相关基础知识,其中对于幻读问题之前一直没有理解深刻,今天就来聊聊「InnoDB 是如何解决幻读的」,话不多说,下面进入主题。

  • 1
    • www.mghio.cn 1 year ago
    • Cache

    InnoDB 是如何解决幻读的

    InnoDB 是如何解决幻读的 2023-04-05 | 数据库 ,

  • 2

    小白必看!轻松理解和解决MySQL幻读问题! 作者:知其然亦知其所以然 2023-06-05 00:28:24 幻读是MySQL数据库中常见的一个问题,但是通过使用适当的方法,我们可以解决这个问题。在本文中,我介绍了两种常用的解决...

  • 5

    幻读是 MySQL 中一个非常普遍,且面试中经常被问到的问题,如果你还搞不懂什么是幻读?什么是 MVCC?以及 MySQL 中的锁?那么请好好收藏和阅读本篇文章,因为它非常重要。 RR 隔离级别 在 MySQL 中,RR 代表 Repeatable Read(可重...

  • 3

    首先我们先介绍一下锁的分类,再进入今天的正题。 一、锁分类: 1.从性能上分:乐观锁、悲观锁。乐观锁(用版本号对比或CAS机制)适用于读比较多的场景,悲观锁适用于写比较多的场景。如果在写比较多的场景使用乐观锁,会导...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK