MySQL数据库的核心MVCC详解

Posted 波波烤鸭

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MySQL数据库的核心MVCC详解相关的知识,希望对你有一定的参考价值。

  本文给大家详细的类介绍下MVCC的内容,MVCC对大家的工作和面试都是非常重要的内容。

一、前置内容

1.ACID

  在看MVCC之前我们先补充些基础内容,首先来看下事务的ACID。

2.mysql的核心日志

  在MySQL数据库中有三个非常重要的日志binlog,undolog,redolog.
  举例演示为:

3.隔离级别

  MySQL的事务隔离级别有四个,分别为:

  事务隔离级别指的是一个事务对数据的修改与另一个并行的事务的隔离程度,当多个事务同时访问相同数据时,如果没有采取必要的隔离机制,就可能发生以下问题:

问题描述
脏读一个事务读到另一个事务未提交的更新数据,所谓脏读,就是指事务A读到了事务B还没有提交的数据,比如银行取钱,事务A开启事务,此时切换到事务B,事务B开启事务–>取走100元,此时切换回事务A,事务A读取的肯定是数据库里面的原始数据,因为事务B取走了100块钱,并没有提交,数据库里面的账务余额肯定还是原始余额,这就是脏读
幻读是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。 同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一个事务的用户发现表中还有没有修改的数据行,就好象 发生了幻觉一样。
不可重复读在一个事务里面的操作中发现了未被操作的数据 比方说在同一个事务中先后执行两条一模一样的select语句,期间在此次事务中没有执行过任何DDL语句,但先后得到的结果不一致,这就是不可重复读
隔离级别描述
READ_UNCOMITTED读未提交(脏读)最低的隔离级别,一切皆有可能。
READ_COMMITED读已提交,ORACLE默认隔离级别,有幻读以及不可重复读风险。
REPEATABLE_READ可重复读,解决不可重复读的隔离级别,但还是有幻读风险。
SERLALIZABLE串行化,最高的事务隔离级别,不管多少事务,挨个运行完一个事务的所有子事务之后才可以执行另外一个事务里面的所有子事务,这样就解决了脏读、不可重复读和幻读的问题了

数据库的并发场景:

  • 读读:不会存在任何问题,也不需要并发控制
  • 读写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读,需要MVCC控制
  • 写写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失

二、MVCC

1.什么是MVCC

  MVCC(Multi-Version Concurrency Control):多版本并发控制,是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。
  MVCC 在 MySQL InnoDB 中的实现主要是为了提高数据库并发性能,用更好的方式去处理读-写冲突,做到即使有读写冲突时,也能做到不加锁,非阻塞并发读。

2.什么是当前读和快照读

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

类型说明
当前读像 select lock in share mode (共享锁), select for update; update; insert; delete (排他锁)这些操作都是一种当前读,为什么叫当前读?就是它读取的是记录的最新版本,读取时还要保证其他并发事务不能修改当前记录,会对读取的记录进行加锁
快照读像不加锁的 select 操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即 MVCC ,可以认为 MVCC 是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本

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

3.三者关系

  当前读,快照度和MVCC的关系是如何的呢?

  1. MVCC 多版本并发控制是(维持一个数据的多个版本,使得读写操作没有冲突) 的概念,只是一个抽象概念,并非实现
  2. 因为 MVCC 只是一个抽象概念,要实现这么一个概念,MySQL 就需要提供具体的功能去实现它,(快照读就是 MySQL 实现 MVCC 理想模型的其中一个非阻塞读功能)。而相对而言,当前读就是悲观锁的具体功能实现
  3. 要说的再细致一些,快照读本身也是一个抽象概念,再深入研究。MVCC 模型在 MySQL 中的具体实现则是由 3 个隐式字段,undo 日志 ,Read View 等去完成的,具体可以看下面的 MVCC 实现原理

4.MVCC的好处

  首先我们要清楚数据库中的并发场景有三种,分别是:

  然后来看MVCC的好处:
  多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。 所以 MVCC 可以为数据库解决以下问题:

  1. 在并发读写数据库时,可以做到在读操作时不用阻塞写操作,写操作也不用阻塞读操作,提高了数据库并发读写的性能
  2. 同时还可以解决脏读,幻读,不可重复读等事务隔离问题,但不能解决更新丢失问题

5.MVCC工作原理

  MVCC 的目的就是多版本并发控制,在数据库中的实现,就是为了解决读写冲突,它的实现原理主要是依赖记录中的 3个隐式字段,undo日志 ,Read View 来实现的。

5.1 三个隐藏字段

  DB_ROW_ID 是数据库默认为该行记录生成的唯一隐式主键,DB_TRX_ID 是当前操作该记录的事务 ID ,而 DB_ROLL_PTR 是一个回滚指针,用于配合 undo日志,指向上一个旧版本
举例如:


  从上面,我们就可以看出,不同事务或者相同事务的对同一记录的修改,会导致该记录的undo log成为一条记录版本线性表,既链表,undo log 的链首就是最新的旧记录,链尾就是最早的旧记录.
undolog:回滚日志,保存了事务发生之前的数据的一个版本,作用:

  1. 可以用于回滚
  2. 同时可以提供多版本并发控制下的读(MVCC),也即非锁定读。
  3. 事务开始之前,将当前事务版本生成 undo log,undo log 也会产生 redo log 来保证 undo log 的可靠性。
  4. 当事务提交之后,undo log 并不能立马被删除,而是放入待清理的链表。
  5. 由 purge 线程判断是否有其它事务在使用 undo 段中表的上一个事务之前的版本信息,从而决定是否可以清理 undo log 的日志空间。

5.2 ReadView

  什么是 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 所在的旧记录就是当前事务能看见的最新老版本.


举例说明:

分析:

第二种情况:

分析:

最后完成的绘图:

总结:能否看到事务修改的数据,取决于可见性算法,可见性算法比较的时候又取决于ReadView中的结果值!因为在不同隔离级别的时候,生成ReadView的时机是不同的RC:每次执行时快照读都会重新生成新的ReadViewRR:只有当第一次事务进行快照读的时候才会生成ReadView,之后的快照读操作都会复用当前的ReadView 。

好了本文就介绍到这里,希望对有有所帮助哦

以上是关于MySQL数据库的核心MVCC详解的主要内容,如果未能解决你的问题,请参考以下文章

mysql mvcc 原理详解

mysql事务-MVCC

MySQL的MVCC底层原理

数据库篇:mysql事务原理之MVCC视图+锁

数据库MVCC详解

MVCC原理探究及MySQL源码实现分析