MySQL事务隔离级别&MVCC原理

MySQL事务隔离级别&MVCC原理

目录

  • MySQL事务隔离级别&MVCC原理
  • 一、MySQL事务隔离级别
    • 1. 脏读
    • 2. 可重复读(Mysql 默认事务级别)
    • 3.不可重复度
    • 4. 幻读
  • 二、什么是MVCC
  • 三、什么是多版本控制
  • 四、什么事当前读和快照读
    • 1. 当前读
    • 2. 快照读
  • 五、MVCC的实现原理
  • 六、UndoLog实现原理
    • 1. insert undo log
    • 2. update undo log
    • 3. Read View(读视图)
      • 3.1 Read View三个属性
      • 3.2 Read View原理分析
      • 3.3 RC,RR级别下的innoDB快照读有什么不同

一、MySQL事务隔离级别

四种:

  1. 读取未提交数据(READ UNCOMMITTED)—脏读的问题
    当前会话可以读取其他会话没有提交数据在去使用,该会话又回滚该行数据----脏读的问题

  2. 读取已提交数据(READ COMMITTED)
    当前会话修改数据,没有提交,但是可以查询最新修改后的数据。
    其他会话查询该数据 ----必须读取已经提交的数据

  3. 可重复读(REPEATABLE READ)
    可重复读 解决幻读问题—缺陷
    中间写操作—发生幻读问题

  4. 串行化(单线程)
    事务的最高级别,在每个读的数据行上,加上锁,使之不可能相互冲突,因此,会导致大量的超时现象
    .
    从上往下,隔离强度逐渐增强,性能逐渐变差。采用哪种隔离级别要根据系统需求权衡决定,其中,可重复读是 MySQL 的默认级别。

1. 脏读

脏读指的是读到了其他事务未提交的数据,未提交意味着这些数据可能会回滚,也就是可能最终不会存到数据库中,也就是不存在的数据。读到了并一定最终存在的数据,这就是脏读。

2. 可重复读(Mysql 默认事务级别)

可重复读指的是在一个事务内,最开始读到的数据和事务结束前的任意时刻读到的同一批数据都是一致的。通常针对数据更新(UPDATE)操作。

可重复读与不可重复读区别
可重复读:当前会话开始事务与结束事务读取到数据都是一致的
不可重复读: 读取到数据最新数据。

3.不可重复度

对比可重复读,不可重复读指的是在同一事务内,不同的时刻读到的同一批数据可能是不一样的,可能会受到其他事务的影响,比如其他事务改了这批数据并提交了。通常针对数据更新(UPDATE)操作。

4. 幻读

幻读是针对数据插入(INSERT)操作来说的。假设事务A对某些行的内容作了更改,但是还未提交,此时事务B插入了与事务A更改前的记录相同的记录行,并且在事务A提交之前先提交了,而这时,在事务A中查询,会发现好像刚刚的更改对于某些数据未起作用,但其实是事务B刚插入进来的,让用户感觉很魔幻,感觉出现了幻觉,这就叫幻读。

幻读:是一次事务中前后数据量发生变化 用户产生不可预料。,

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级别下确实如此。

事务隔离其实就是为了解决上面提到的脏读、不可重复读、幻读这几个问题,下面展示了 4 种隔离级别对这三个问题的解决程度。

事务隔离级别 脏读 不可重复读 幻读
读未提交(read-uncommitted) ru
不可重复读(read-committed) rc
可重复读(repeatable-read) rr
串行化(serializable) sz

二、什么是MVCC

MVCC,全称Multi-Version Concurrency Control,即多版本并发控制。MVCC是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问,在编程语言中实现事务内存。


三、什么是多版本控制

多版本控制: 指的是一种提高并发的技术。最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提高了InnoDB的并发度。在内部实现中,与Postgres在数据行上实现多版本不同,InnoDB是在undolog中实现的,通过undolog可以找回数据的历史版本。找回的数据历史版本可以提供给用户读(按照隔离级别的定义,有些读请求只能看到比较老的数据版本),也可以在回滚的时候覆盖数据页上的数据。在InnoDB内部中,会记录一个全局的活跃读写事务数组,其主要用来判断事务的可见性,MVCC是一种多版本并发控制机制。


四、什么事当前读和快照读

1. 当前读

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

Select lock in share mode(共享锁)
间隙锁 行锁实现

2. 快照读

像不加锁的select操作就是快照读,即不加锁的非阻塞读;快照读的前提是隔离级别不是串行级别,串行级别下的快照读会退化成当前读;之所以出现快照读的情况,是基于提高并发性能的考虑,快照读的实现是基于多版本并发控制,即MVCC,可以认为MVCC是行锁的一个变种,但它在很多情况下,避免了加锁操作,降低了开销;既然是基于多版本,即快照读可能读到的并不一定是数据的最新版本,而有可能是之前的历史版本
快照读:就是普通的 select 查询SQL语句-----readview


五、MVCC的实现原理

MVCC的目的就是多版本并发控制,在数据库中的实现,就是为了解决读写冲突,它的实现原理主要是依赖记录中的 3个隐式字段,undo日志 ,Read View 来实现的
DB_TRX_ID(事务id)、DB_ROLL_PTR(回滚指针)、(row_id)

1.DB_TRX_ID
6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID
2.DB_ROLL_PTR
7byte,回滚指针,指向这条记录的上一个版本(存储于rollback segment里)
3.DB_ROW_ID
6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以DB_ROW_ID产生一个聚簇索引

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


六、UndoLog实现原理

undo log主要分为两种

1. insert undo log

代表事务在insert新记录时产生的undo log, 只在事务回滚时需要,并且在事务提交后可以被立即丢弃

2. update undo log

1.为了实现 InnoDB 的 MVCC 机制,更新或者删除操作都只是设置一下老记录的 deleted_bit ,并不真正将过时的记录删除。

2.为了节省磁盘空间,InnoDB 有专门的 purge 线程来清理 deleted_bit 为 true 的记录。为了不影响 MVCC 的正常工作,purge 线程自己也维护了一个read view(这个 read view 相当于系统中最老活跃事务的 read view );如果某个记录的 deleted_bit 为 true ,并且 DB_TRX_ID 相对于 purge 线程的 read view 可见,那么这条记录一定是可以被安全清除的。

3. Read View(读视图)

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

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

3.1 Read View三个属性

  1. 活跃事务列表m_ids----没有提交事务的事务。
    一个数值列表,用来维护Read View生成时刻系统正活跃的事务ID
  2. min_trx_id
    记录trx_list列表中事务ID最小的ID
  3. max_trx_id
    ReadView生成时刻系统尚未分配的下一个事务ID,也就是目前已出现过的事务ID的最大值+1
  4. create_trx_d 表示生成该ReadView的事务的id

3.2 Read View原理分析

判断版本链:

  1. 判断trx_id是否等于create_trx_id,如果等于该事务id 则
    说明 是当前我们自己的session会话 修改数据,可以直接访问;
  2. 判断trx_id
  3. 判断trx_id>max_trx_id 说明该事务是在readview 生成以后开启
    不允许访问。
  4. min_trx_id<=判断trx_id <=max_trx_id(),成立在m_ids数据中对比
    ,不存在活跃列中的数据则代表数据已经提交,可以直接访问的。

3.3 RC,RR级别下的innoDB快照读有什么不同

1.在RR级别下的某个事务的对某条记录的第一次快照读会创建一个快照及Read View, 将当前统活跃的其他事务记录起来,此后在调用快照读的时候,还是使用的是同一个Read View,所以只要当前事务在其他事务提交更新之前使用过快照读,那么之后的快照读使用的都是同一个Read View,所以对之后的修改不可见;
2.即RR级别下,快照读生成Read View时,Read View会记录此时所有其他活动事务的快照,这些事务的修改对于当前事务都是不可见的。而早于Read View创建的事务所做的修改均是可见
3.而在RC级别下的,事务中,每次快照读都会新生成一个快照和Read View, 这就是我们在RC级别下的事务中可以看到别的事务提交的更新的原因

MVCC 不是使用锁的形式来对数据实现隔离的,幻读问题 如果想解决
幻读问题,就直接采用间隙锁即可。

当两次快照读之间存在当前读,ReadView 会重新生成导致发生 幻读的问题。
ReadLog

你可能感兴趣的