MYSQL的幻读和我们平常说的幻读有什么区别?
一、MYSQL的幻读和我们平常说的幻读有什么区别
平常说的幻读:
事务1查询id<10的记录时,返回了2条记录,接着事务2插入了一条id为3的记录,并提交。接着事务1查询id<10的记录时,返回了3条记录,结果多了一条数据。由于Mysql存在MVCC,解决了这种情况下的幻读。
Mysql的幻读:
Mysql的幻读,并不是说两次读取获取的结果集不同,幻读侧重的方面是某一次的 select 操作得到的结果所表征的数据状态无法支撑后续的业务操作。更为具体一些:select 某记录是否存在,不存在,准备插入此记录,但执行 insert 时发现此记录已存在,无法插入,此时就发生了幻读。
举个例子:事务T1:
事务T2:
step1 T1: SELECT * FROM users WHERE id = 1;step2 T2: INSERT INTO users VALUES (1, ‘big cat’);step3 T1: INSERT INTO users VALUES (1, ‘big cat’);step4 T1: SELECT * FROM users WHERE id = 1;T1 :主事务,检测表中是否有 id 为 1 的记录,没有则插入,这是我们期望的正常业务逻辑。T2 :干扰事务,目的在于扰乱 T1 的正常的事务执行。
在 RR 隔离级别下,step1、step2 是会正常执行的,step3 则会报错主键冲突,对于 T1 的业务来说是执行失败的,这里 T1 就是发生了幻读,因为 T1 在 step1 中读取的数据状态并不能支撑后续的业务操作,T1:“见鬼了,我刚才读到的结果应该可以支持我这样操作才对啊,为什么现在不可以”。T1 不敢相信的又执行了 step4,发现和 setp1 读取的结果是一样的(RR下的 MMVC机制)。此时,幻读无疑已经发生,T1 无论读取多少次,都查不到 id = 1 的记录,但它的确无法插入这条他通过读取来认定不存在的记录(此数据已被T2插入),对于 T1 来说,它幻读了。其实产生幻读的原因就是:行锁只能锁住行,即使把所有的行记录都上锁,也阻止不了新插入的记录。
二、MySQL 是如何解决幻读的
1、多版本并发控制(MVCC)(快照读/一致性读)
多数数据库都实现了多版本并发控制,并且都是靠保存数据快照来实现的。以 InnoDB 为例。可以理解为每一行中都冗余了两个字段,一个是行的创建版本,一个是行的删除(过期)版本。具体的版本号(trx_id)存在 information_schema.INNODB_TRX 表中。版本号(trx_id)随着每次事务的开启自增。事务每次取数据的时候都会取创建版本小于当前事务版本的数据,以及过期版本大于当前版本的数据。普通的 select 就是快照读。
select * from T where number = 1;
原理:将历史数据存一份快照,所以其他事务增加与删除数据,对于当前事务来说是不可见的。
2、next-key 锁 (当前读)
next-key 锁包含两部分
记录锁(行锁)间隙锁记录锁是加在索引上的锁,间隙锁是加在索引之间的。(思考:如果列上没有索引会发生什么?)
select * from T where number = 1 for update;select * from T where number = 1 lock in share mode;insertupdatedelete
原理:将当前数据行与上一条数据和下一条数据之间的间隙锁定,保证此范围内读取的数据是一致的。
3、MySQL InnoDB 引擎 RR 隔离级别是否解决了幻读
Mysql官方给出的幻读解释是:只要在一个事务中,第二次select多出了row就算幻读。a事务先select,b事务insert确实会加一个gap锁,但是如果b事务commit,这个gap锁就会释放(释放后a事务可以随意dml操作),a事务再select出来的结果在MVCC下还和名列前茅次select一样,接着a事务不加条件地update,这个update会作用在所有行上(包括b事务新加的),a事务再次select就会出现b事务中的新行,并且这个新行已经被update修改了,实测在RR级别下确实如此。如果这样理解的话,Mysql的RR级别确实防不住幻读。
在快照读读情况下,mysql通过mvcc来避免幻读。
在当前读读情况下,mysql通过next-key来避免幻读。
select * from t where a=1;属于快照读
select * from t where a=1 lock in share mode;属于当前读
不能把快照读和当前读得到的结果不一样这种情况认为是幻读,这是两种不同的使用方式。所以我认为mysql的rr级别是解决了幻读的。
先说结论,MySQL 存储引擎 InnoDB 隔离级别 RR 解决了幻读问题。不能把快照读和当前读得到的结果不一样这种情况认为是幻读,这是两种不同的使用方式。所以认为 MySQL 的 RR 级别是解决了幻读的。先说结论,MySQL 存储引擎 InnoDB 隔离级别 RR 解决了幻读问题。
如引用一问题所说,T1 select 之后 update,会将 T2 中 insert 的数据一起更新,那么认为多出来一行,所以防不住幻读。但是其实这种方式是一种 bad case。如图:
延伸阅读1:MySQL简介
MySQL是一个关系型数据库管理系统,由瑞典MySQL AB 公司开发,属于 Oracle 旗下产品。MySQL 是最流行的关系型数据库管理系统之一,在 WEB 应用方面,MySQL是较好的 RDBMS (Relational Database Management System,关系数据库管理系统) 应用软件之一。

相关推荐HOT
更多>>
空间大小命令linux?
linux如何查看磁盘空间大小,linux如何查看磁盘空间大小变化记录_百度...linux怎么查询磁盘空间呢,下面就让我们来看看吧。打开linux系统,在lin...详情>>
2023-10-21 23:28:48
linux系统杀掉命令?
Linux终止前台进程的命令1、首先,连接相应linux主机,进入到linux命令行状态下,等待输入shell指令。其次,以终止进程号1984的nginx子进程为例...详情>>
2023-10-21 22:28:44
linux命令切换窗口?
linux如何用命令行更改每个窗口的位置,第一次启动linux系统会进入命令行模式,会要求输入用户名,默认的管理员帐号名为root,输入“root”后回...详情>>
2023-10-21 20:57:44
linux移除程序命令?
linux的文件和目录的删除命令有哪些?1、Linux删除文件的命令是rm命令。Linuxrm命令用于删除一个文件或者目录。语法:rm[options]name...参数:-...详情>>
2023-10-21 19:54:21