前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >mysql是如何解决幻读

mysql是如何解决幻读

作者头像
小土豆Yuki
发布2021-11-16 14:29:45
7140
发布2021-11-16 14:29:45
举报
文章被收录于专栏:洁癖是一只狗

mysql隔离级别在面试中经常出现,今天我就分析一下这几个隔离级别,mysql提供了四种隔离级别,以及解决可以解决哪种问题,如下图

隔离级别

脏读

不可重复读

幻读

读未提交

不能解决

不能解决

不能解决

读已提交

能解决

不能解决

不能解决

可重复读

能解决

能解决

能解决

串行化

能解决

能解决

能解决

特别说明,mysql默认隔离级别是可重复读,但是网上有很多说法可重复读不能解决幻读问题,但是mysql的可重复读是可以解决幻读的问题,后面会说到

首先我们明确几个概念

脏读:

有两个事务A,B,事务A仅仅是更新了某行数据,但是没有提交,但是事物B确读取到了事务A更新的数据,此时事务A有进行了回滚,就会导致事务B读取到错误的数据

不可重复读:有两个事务A,B,事务B开始读取某一行数据例如id=1,age=1,但是此时事务A更新了id=1,age=10,但是此时事务B再次读取发现id=1,age=10,即同一个事务不同时刻读取到了不同的数据

可重复读:

一个事物开始知道事务结束前读物的数据都是相同的

幻读:有两个事务A,B,但是事物B某时刻读取到id=1,age=1有一条数据,但是在第二次读取的时候,事务A,插入了id=1,age=10,然后事物B第二次读取的时候发现id=1居然有两条数据,是不是产生幻觉了

其次我们说明一下事务隔离级别

前提环境是数据库有一张表如下

代码语言:javascript
复制
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(30) DEFAULT NULL,
`age` tinyint(4) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8
insert into user('name','age') values ('张三',1);

读未提交:

事务是靠锁来时实现的,但是读未提交是没有使用锁的,因此他是性能最好的,他的含义如下图

事务A在更新一条数据之后,但是没有提交,而此时事务B读取到这条数据,此时事务A又进行了更新,然后事物B拿着错误数据操作业务

读已提交:

这个隔离级别解决了上面的脏读,具体如下图

两个事务A,B,在事务B中只能读取到事务A提交的数据

可重复读

就是在同一个事务中,事务开始和事务结束之前读取的的数据都是一致的

如下图

事务B在事务开始直到事务结束读取的数据都是一致的,不管事务是否提交.

mysql是如何实现可重读的的呢,也是面试中常问的事情,今天也说明一下

其实mysql是使用MVCC(多版本控制实现),在数据库看到的数据实际上可能存在多份数据,而这每一份数据,都会生成一个事物id,全局唯一的,正如下图,存在三个版本的数据,同时每一个事务开始都是会生成一个快照,即一致性视图,可重读即事务开始产生一个全局事务快照,而读已提交每次执行都会产生一个新快照

重点是快照的读取规则

  1. 当前事务的更新,可以读取到
  2. 当前版本未提交,不能读取到
  3. 版本提交,但是快照在之后创建可以读取到
  4. 版本提交,但是快照之前创建,不可以读取到

串行化

串行化是这里面最厉害的一种隔离级别,他把所有事务都搞成了顺序执行,就行但是单线程执行了,性能很差

幻读

这里说明一下幻读的含义,如下图

在事物A中,开始读取id=1,只有一条数据,但是在第二次读取之前,事务B插入了一条数据同样是id=1,然后事物A再次读取的时候就是发现有两条数据了

最后注重说明一下如何解决幻读问题

我们知道在高并发环境中,可能存在这种情况,两个事务A,B,对同一条数据进行修改,谁最后执行就按照谁的结果处理,并且我们知道事务更新之前先要读取数据,而这个读取数据,是当前读,即在读取多版本哪种最新的那一条数据

如下图

事务A,在更新id=1的时候,此时事务B也要更新id=1,但是此时事务会给id=1加上行锁,此时事务B只能等待,直到事务A提交释放,事务B才会执行,

而我们mysql使用的重复读隔离级别是使用行锁和间隙锁结合的next-key锁解决的幻读

如上图,假设数据有两条数据

id

name

age

1

张三

10

2

李四

30

然后我们按照下面逻辑执行

当时事务A执行update user set name='张思' where age=10的时候在age=10加入了行锁并且加入了间隙锁(10,30](负无穷,10],因此事务B在执行插入的时候就会被阻塞,直到事务A被提交释放才会执行。

由于age是有索引的,此时只有age>10的数据不会被阻塞,当age没有索引的时候,就会个整个表加上间隙锁和行锁,导致整个表插入数据都会被阻塞,直到事务A提交释放

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-11-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 洁癖是一只狗 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 SQL Server
腾讯云数据库 SQL Server (TencentDB for SQL Server)是业界最常用的商用数据库之一,对基于 Windows 架构的应用程序具有完美的支持。TencentDB for SQL Server 拥有微软正版授权,可持续为用户提供最新的功能,避免未授权使用软件的风险。具有即开即用、稳定可靠、安全运行、弹性扩缩等特点。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档