MYSQL隔离级别和MVCC

Mysql四种隔离级别和存在的安全问题

在这里插入图片描述
read-uncommitted 读未提交:
在该级别,所有的事务都可以看到其他未提交事务的执行结果,本隔离级别很少用于实际应用,因为它的性能不比其他级别好多少。读到其他事务还未提交的数据,也称之为脏读

read-committed 读提交内容:
这是大多数数据库系统的默认隔离级别(但不是MYSQL默认的),它满足了隔离的简单定义:一个事务只能看见已提交事务所做的改变。多次读出来的结果不一样,包含修改(主要)删除/新增,即所谓的不可重复读。

repeatable-read 可重读:
是MYSQL默认的,确保统一事务的多个实例在并发读取数据时,会看到同样的数据行。

serializable 可串行化:
这是最高的隔离级别,他通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简而言之,他是在每个读的数据行上加上共享锁。在这个级别可能导致大量的超时现象和锁竞争。
不可重复读侧重于修改,幻读侧重于新增或删除。解决不可重复读的问题只需锁住满足条件的行,解决幻读需要锁表

什么是当前读和快照读?

在学习 MVCC 多版本并发控制之前,我们必须先了解一下,什么是 MySQL InnoDB 下的当前读和快照读?

  • 当前读(加锁读)
    像 select lock in share mode (共享锁), select for update; update; insert; delete (排他锁)这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁

  • 快照读
    不加锁的 select 操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即 MVCC ,可以认为 MVCC 是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本。【快照读就是 MySQL 实现 MVCC 理想模型的其中一个非阻塞读功能

说白了 MVCC 就是为了实现读-写冲突不加锁,而这个读指的就是快照读, 而非当前读,当前读实际上是一种加锁的操作,是悲观锁的实现。

MVCC 的实现原理

MVCC 模型在 MySQL 中的具体实现则是由 3 个隐式字段,undo 日志 ,Read View 等去完成的。
隐式字段:
DB_TRX_ID
6 byte,最近修改**(修改/插入)事务 ID**:记录创建这条记录/最后一次修改该记录的事务 ID
DB_ROLL_PTR
7 byte,回滚指针,指向这条记录的上一个版本(存储于 rollback segment 里)
DB_ROW_ID
6 byte,隐含的自增 ID(隐藏主键),如果数据表没有主键,InnoDB 会自动以DB_ROW_ID产生一个聚簇索引
实际还有一个删除 flag 隐藏字段, 既记录被更新或删除并不代表真的删除,而是删除 flag 变了
undolog
undo log 主要分为两种:

insert undo log
代表事务在 insert 新记录时产生的 undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃
update undo log
事务在进行 update 或 delete 时产生的 undo log ; 不仅在事务回滚时需要,在快照读时也需要;所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被 purge 线程统一清除。
对 MVCC 有帮助的实质是 update undo log ,undo log 实际上就是存在 rollback segment 中旧记录链

Read View 读视图

Read View 就是事务进行快照读操作的时候生产的读视图 (Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照,记录并维护系统当前活跃事务的 ID (当每个事务开启时,都会被分配一个 ID , 这个 ID 是递增的,所以最新的事务,ID 值越大)

所以我们知道 Read View 主要是用来做可见性判断的, 即当我们某个事务执行快照读的时候,对该记录创建一个 Read View 读视图,把它比作条件用来判断当前事务能够看到哪个版本的数据,既可能是当前最新的数据,也有可能是该行记录的undo log里面的某个版本的数据。

Read View遵循一个可见性算法,主要是将要被修改的数据的最新记录中的 DB_TRX_ID(即当前事务 ID )取出来,与系统当前其他活跃事务的 ID 去对比(由 Read View 维护),如果 DB_TRX_ID 跟 Read View 的属性做了某些比较,不符合可见性,那就通过 DB_ROLL_PTR 回滚指针去取出 Undo Log 中的 DB_TRX_ID 再比较,即遍历链表的 DB_TRX_ID(从链首到链尾,即从最近的一次修改查起),直到找到满足特定条件的 DB_TRX_ID , 那么这个 DB_TRX_ID 所在的旧记录就是当前事务能看见的最新老版本。
视图生成的策略:比较undolog中的
RC , RR 级别下的 InnoDB 快照读有什么不同?
正是 Read View 生成时机的不同,从而造成 RC , RR 级别下快照读的结果的不同

  • 在 RR 级别下的某个事务的对某条记录的第一次快照读会创建一个快照及 Read View, 将当前系统活跃的其他事务记录起来,此后在调用快照读的时候,还是使用的是同一个 Read View,所以只要当前事务在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个 Read View,所以对之后的修改不可见;

  • 即 RR 级别下,快照读生成 Read View 时,Read View 会记录此时所有其他活动事务的快照,这些事务的修改对于当前事务都是不可见的。而早于Read View创建的事务所做的修改均是可见

  • 而在 RC 级别下的,事务中,每次快照读都会新生成一个快照和 Read View , 这就是我们在 RC 级别下的事务中可以看到别的事务提交的更新的原因

总之在 RC 隔离级别下,是每个快照读都会生成并获取最新的 Read View;而在 RR 隔离级别下,则是同一个事务中的第一个快照读才会创建 Read View, 之后的快照读获取的都是同一个 Read View
————————————————
版权声明:本文为CSDN博主「SnailMann」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/SnailMann/article/details/94724197

MYSQL如何解决不可重复读和幻读问题

RC状态下即可能出现不可重复读问题

  • MySQL中,默认使用的事务隔离界别是可重复读,为了解决不可重复读问题,InnoDB采用了MVCC(多版本并发控制)【基于乐观锁】来解决!
  • MVCC(多版本并发控制)是利用在每条数据后面加了隐藏的两列(创建版本号和删除版本号),每个事务在开始的时候都会有一个递增的当前事务版本号!

MVCC在RC状态下,事务在开始的时候会生成一个ReadView,包含由隐藏字段等创建的事务ID信息,后续查询时候也会始终只看到ReadView之内的数据,对于增删操作事务ID会超过当前ReadView中的事务ID,天然解决幻读。对于修改操作每次数据被修改后数据的事务ID会自增,超过当前ReadView中记录的数据范围,所以也会解决,所以MYSQL是通过MVCC的ReadView和隐藏的当前事务版本号字段配合解决不可重复读问题的。

对于RR状态下的幻读问题

先认识InnoDB中有如下三种锁:

  • Record Lock:对单个记录加锁
  • Gap Lock:间隙锁,锁住记录前面的间隙,不允许插入记录
  • Next-key Lock:同时锁住数据和数据前面的间隙,即数据和数据前面的间隙都不允许插入记录

通过next-key-lock间隙锁和ReadView机制保证,MVCC在快照读场景下必然不会看到新插入的行,所以天然就解决了幻读的问题。但是对于当前读MVCC 是无法解决的。需要使用 Gap Lock 或 Next-Key Lock(Gap Lock + Record Lock)+MVCC来解决。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值