你能讲下mysql 的mvcc 吗?|MySQL 系列(8)

qezhu521
发布于 2022-4-20 15:43
浏览
0收藏

作者 | 小汪哥写代码
来源 | 小汪哥(ID:xwgcoding)

“ 面试中经常遇到这个问题,那你知道吗?阅读本文,你将了解到:

互联网项目mysql一般用什么隔离级别?

MVCC的原理

MVCC 能不能解决幻读问题?”

前言

我们知道,所谓MVCC (Multi-Version Concurrency Control ,多版本并发控制)可以提高不同事物的读写、写读操作的并发执行的系统性能,那么为什么会有mvcc就要从事物并发执行会有什么问题开始聊了。

事务并发执行遇到的问题

第二部分的正文内容从这里开始。mysql 是一个c/s 的 服务器架构软件,客户端的每一个连接就是一个会话,正常来讲如果会话排队执行,那么就没有问题。但是这样性能也太差了。如果并发执行多个会话,就会舍弃一部分隔离性。一般情况下有以下几种隔离级别:

脏写(Dirty Write)

  • 如果一个事务修改了另一个未提交事务修改过的数据,那就意味着发生了脏写

脏读(Dirty Read)

  • 如果一个事务读到了另一个未提交事务修改过的数据,那就意味着发生了脏读

不可重复读(Non-Repeatable Read)

  • 如果一个事务只能读到另一个已经提交的事务修改过的数据,并且其他事务每对该数据进行一次修改并提交后,该事务都能查询得到最新值,那就意味着发生了不可重复读

幻读(Phantom)

  • 如果一个事务先根据某些条件查询出一些记录,之后另一个事务又向表中插入了符合这些条件的记录,原先的事务再次按照该条件查询时,能把另一个事务插入的记录也读出来,那就意味着发生了幻读

SQL标准中的四种隔离级别

  • READ UNCOMMITTED:未提交读。(RU)
  • READ COMMITTED:已提交读。(RC)
  • REPEATABLE READ:可重复读。(RR)
  • SERIALIZABLE:可串行化。

RU 隔离级别下,可能发生脏读、不可重复读和幻读问题。

RC 隔离级别下,可能发生不可重复读和幻读问题,但是不可以发生脏读问题。

RR 隔离级别下,可能发生幻读问题,但是不可以发生脏读和不可重复读的问题。

SERIALIZABLE 隔离级别下,各种问题都不可以发生。性能最差。

数据库必须实现SQL标准吗?

不同的数据库厂商对SQL标准中规定的四种隔离级别支持不一样,比方说Oracle就只支持READ COMMITTED和SERIALIZABLE隔离级别。

MySQL的默认隔离级别为REPEATABLE READ (RR)。

互联网项目mysql一般用什么隔离级别?

在互联网项目中一般用读已提交(Read Commited)这个隔离级别,项目中是不用读未提交(Read UnCommitted)和串行化(Serializable)两个隔离级别,有以下两个原因:

  • 采用读未提交(Read UnCommitted),一个事务读到另一个事务未提交读数据,这个不用多说吧,从逻辑上都说不过去!
  • 采用串行化(Serializable),每个次读操作都会加锁,快照读失效,一般是使用mysql自带分布式事务功能时才使用该隔离级别!是强一致性事务,性能不佳!互联网的分布式方案,多采用最终一致性的事务解决方案!

为什么选读已提交(Read Commited)作为事务隔离级别?

  • 在互联网项目中,读到其他已经提交的事物基本上是可以理解的。
  • 在RR隔离级别下,条件列未命中索引会锁表!而在RC隔离级别下,只锁行。
  • 因为MySQL的RR需要gap lock来解决幻读问题。而RC隔离级别则是允许存在不可重复读和幻读的。所以RC的并发一般要好于RR。而且导致出现死锁的几率比RC大的多!
  • 在RC隔离级别下,半一致性读(semi-consistent)特性增加了update操作的并发性!“ 半一致性读:在 RC 事务隔离级别下,Update 语句可以利用到半一致性读的特性,会多进行一次判断,当 where 条件匹配到的记录与当前持有锁的事务中的记录不冲突时,就会提前释放 InnoDB 锁,虽然这样做违背了二阶段加锁协议,但却可以减少锁冲突,提高事务并发能力,是一种很好的优化行为。”

关于锁的问题我们后面分享

MVCC的原理

终于要讲MVCC了,mysql 应用这么广泛,和它的性能好有一定的关系,性能又和MVCC有关系。上面我们了解的 mysql 的事物隔离级别,其实我觉得MVCC 主要作用于RR 和RC 两种事物隔离级别。

版本链

想要了解MVCC 就必须知道版本链。

对于使用InnoDB存储引擎的表来说,它的聚簇索引记录中都包含两个必要的隐藏列(row_id并不是必要的,我们创建的表中有主键或者非NULL的UNIQUE键时都不会包含row_id列):

  • trx_id:每次一个事务对某条聚簇索引记录进行改动时,都会把该事务的事务id赋值给trx_id隐藏列。
  • roll_pointer:每次对某条聚簇索引记录进行改动时,都会把旧的版本写入到undo日志中,然后这个隐藏列就相当于一个指针,可以通过它来找到该记录修改前的信息。

你能讲下mysql 的mvcc 吗?|MySQL 系列(8)-鸿蒙开发者社区

每次对记录进行改动,都会记录一条undo日志,每条undo日志也都有一个roll_pointer属性 (INSERT操作对应的undo日志没有该属性,因为该记录并没有更早的版本),可以将这些undo日志都连起来,串成一个链表,所以现在的情况就像下图一样:你能讲下mysql 的mvcc 吗?|MySQL 系列(8)-鸿蒙开发者社区

ReadView 数据结构

前面说为什么我觉得 MVCC 主要作用于RC 和RR 的隔离级别呢?

对于使用RU隔离级别的事物来说,由于可以读到未提交的事物修改过的的记录,所以直接读取记录的最新版本就行。

对于SERIALIZABLE的隔离级别,Innodb 使用加锁的方式来访问记录。

ReadView 数据结构几个重要的属性:

  • m_ids:表示在生成ReadView时当前系统中活跃的读写事务的事务id列表。
  • min_trx_id:表示在生成ReadView时当前系统中活跃的读写事务中最小的事务id,也就是m_ids中的最小值。
  • max_trx_id:表示生成ReadView时系统中应该分配给下一个事务的id值。
  • creator_trx_id:表示生成该ReadView的事务的事务id。

版本数据是否可见的判断步骤:

  1. trx_id=creator_trx_id;如果被访问版本的trx_id属性值与ReadView中的creator_trx_id值相同,意味着当前事务在访问它自己修改过的记录,所以该版本可以被当前事务访问。
  2. trx_id<MIN_TRX_ID;如果被访问版本的TRX_ID属性值小于READVIEW中的MIN_TRX_ID值,表明生成该版本的事务在当前事务生成READVIEW前已经提交,所以该版本可以被当前事务访问。< p>
  3. trx_id>max_trx_id;如果被访问版本的trx_id属性值大于或等于ReadView中的max_trx_id值,表明生成该版本的事务在当前事务生成ReadView后才开启,所以该版本不可以被当前事务访问。
  4. min_trx_id<TRX_ID<MAX_TRX_ID;如果被访问版本的TRX_ID属性值在READVIEW的MIN_TRX_ID和MAX_TRX_ID之间,那就需要判断一下TRX_ID属性值是不是在M_IDS列表中,如果在,说明创建READVIEW时生成该版本的事务还是活跃的,该版本不可以被访问;如果不在,说明创建READVIEW时生成该版本的事务已经被提交,该版本可以被访问。< p>

RR 和 RC 隔离级别的不同:

生成ReadView的时机不同:

  • RC 在每一次进行普通SELECT操作前都会生成一个ReadView,每次生成新的m_ids、min_trx_id、max_trx_id、creator_trx_id 属性。所以不能可重复读。
  • RR 只在第一次进行普通SELECT操作前生成一个ReadView,之后的查询操作都重复使用这个ReadView。m_ids、min_trx_id、max_trx_id、creator_trx_id 属性是一样的。所以实现了可重复读。

MVCC 不能解决幻读问题

MVCC不能解决幻读问题,不然RR 的隔离级别就可以叫可幻读了。例如

begin;
#假设users表为空,下面查出来的数据为空
select * from users; #没有加锁
#此时另一个事务提交了,且插入了一条id=1的数据
select * from users; #读快照,查出来的数据为空
update users set name='mysql' where id=1;#update是当前读,所以更新成功,并生成一个更新的快照
select * from users; #读快照,查出来id为1的一条记录,因为MVCC可以查到当前事务生成的快照
commit;

可以看到前后查出来的数据行不一致,发生了幻读。所以说只有MVCC是不能解决幻读问题的,解决幻读问题靠的是间隙锁。关于锁的问题我们下一次分享。所以RR级别下想解决幻读问题,需要我们显式加锁。

可以看到,InnoDB通过MVCC很好的解决了读写冲突的问题,大大提升了数据库的并发能力。这样的MVCC 你了解了吗?

标签
收藏
回复
举报
回复
    相关推荐