设为首页 加入收藏

TOP

理解MVCC(一)
2023-07-25 21:36:14 】 浏览:67
Tags:理解 MVCC

MVCC

就是多版本并发控制。MVCC 是一种并发控制的方法,一般在数据库管理系统中,实现对数据库的并发访问。

为什么需要MVCC呢?数据库通常使用锁来实现隔离性。最原生的锁,锁住一个资源后会禁止其他任何线程访问同一个资源。但是很多应用的一个特点都是读多写少的场景,很多数据的读取次数远大于修改的次数,而读取数据间互相排斥显得不是很必要。所以就使用了一种读写锁的方法,读锁和读锁之间不互斥,而写锁和写锁、读锁都互斥。这样就很大提升了系统的并发能力。之后人们发现并发读还是不够,又提出了能不能让读写之间也不冲突的方法,就是读取数据时通过一种类似快照的方式将数据保存下来,这样读锁就和写锁不冲突了,不同的事务session会看到自己特定版本的数据。当然快照是一种概念模型,不同的数据库可能用不同的方式来实现这种功能。

理解MVCC

什么是MVCC

全称Multi-Version Concurrency Control,即多版本并发控制,主要是为了提高数据库的并发性能。PS:基于InnoDB引擎的默认事务机制可重复读来讲的,因为Mylsam不支持事务。

同一行数据平时发生读写请求时,会上锁阻塞住。当读为快照读时mvcc用更好的方式去处理读—写请求,做到在发生读—写请求冲突时不用加锁

select .. for update 即当前读是一种加锁操作,是悲观锁

那它到底是怎么做到读—写不用加锁的,快照读当前读又是什么。

快照读与当前读的区别:

当前读:它读取的数据都是当前最新的数据,会对读取到的数据进行加锁,防止其他事务修改其数据,是悲观锁的一种,这里不做展开。

例如如下操作:

  • select fro update
  • update
  • insert
  • delete
  • select lock in share mode
  • 事务隔离级别为串行化时都加锁

快照读:最基础的不加锁的select操作

快照读的实现是基于多版本并发控制,即MVCC,既然是多版本,那么快照读读到的数据不一定是当前最新的数据,有可能是之前历史版本的数据。

快照读与mvcc的关系

MVCCC是“维持一个数据的多个版本,使读写操作没有冲突”的一个抽象概念

这个概念需要具体功能去实现,这个具体实现就是快照读

数据库并发场景

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

MVCC解决并发哪些问题?

mvcc用来解决读—写冲突的无锁并发控制,就是为事务分配单向增长时间戳。为每个数据修改保存一个版本,版本与事务时间戳相关联

读操作只读取该事务开始前数据库快照

解决问题如下:

  • 并发读-写时:可以做到读操作不阻塞写操作,同时写操作也不会阻塞读操作。
  • 解决脏读、大部分幻读不可重复读等事务隔离问题,但不能解决上面的写-写 更新丢失问题。(PS:不能完全解决幻读)

MVCC的实现原理

它的实现原理主要是版本链undo日志Read View 来实现的

版本链:

在InnoDB引擎表中,它的聚簇索引记录中有两个必要的隐藏列

  • trx_id这个id用来存储的每次对某条聚簇索引记录进行修改的时候的事务id。

  • roll_pointe每次对哪条聚簇索引记录有修改的时候,都会把老版本写入undo日志中。这个roll_pointer就是存了一个指针,它指向这条聚簇索引记录的上一个版本的位置,通过它来获得上一个版本的记录信息。(注意插入操作的undo日志没有这个属性,因为它没有老版本)(实现版本链的关键)

  • row_id,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以db_row_id产生一个聚簇索引

  • 实际还有一个删除flag隐藏字段, 记录被更新删除并不代表真的删除,而是删除flag变了

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

若此时执行下列语句。

更新后的版本链:

undo日志

Undo log 主要用于记录数据被修改之前的日志,在表信息修改之前先会把数据拷贝到undo log里。

事务进行回滚时可以通过undo log 里的日志进行数据还原

Undo log 的用途

  • 保证事务进行rollback时的原子性和一致性,当事务进行回滚的时候可以用undo log的数据进行恢复
  • 用于MVCC快照读的数据,在MVCC多版本控制中,通过读取undo log历史版本数据可以实现不同事务版本号都拥有自己独立的快照数据版本

undo log主要分为两种:

  • insert undo log

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

  • update undo log(主要)

    事务在进行update或delete时产生的undo log ; 不仅在事务回滚时需要,在快照读时也需要;

    所以不能随便删除,只有在快速读或事务回滚不涉及该日志时,对应的日志才会被purge线程统一清除

Read View(读视图)

事务进行快照读操作的时候生产的读视图(Read View),在该事务执行的快照读的那一刻,会生成数据库系统当前的一个快照

记录并维护系统当前活跃事务的ID(没有commit,当每个事务开启时,都会被分配一个ID, 这个ID是递增的,所以越新的事务,ID值越大),是系统中当前不应该被本事务看到的其他事务id列表

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

Read View几个属性

  • trx_ids: 当前系统活跃(未提交)事务版本号集合。
  • low_limit_id: 创建当前read view 时“当前系统最大事务版本号+1”。
  • up_limit_id: 创建当前read view 时“系统正处于活跃事务最小版本号
  • creator_trx_id: 创建当前read view的事务版本号;

一个事务去访问记录的时候,除了自己的更新记录总是可见之外,还有这几种情况:

  • 如果记录的 trx_id 值小于 Read View 中的 min_trx_id 值,表示这个版本的记录是在创建 Read View 已经提交的事务生成的,所以该版本的记录对当前事务可见
  • 如果记录的 trx_id 值大于等于 Read View 中的 max_trx_id 值,表示这个版本的记录是在创建 Read View 才启动的事务生成的,所以该版本的记录对当前事务不可见
  • 如果记录的 trx_id 值在 Read View 的min_trx_idmax_trx_id之间,需要判断 trx_id是否在m_ids 列表中:
    • 如果记录的 trx_id m_ids 列表中,表示生成该版本记录的活跃事务依然活跃着(还没提交事务),所以该版本的记录对当前事务不可见
    • 如果记录的 trx_id 不在 m_ids列表中,表示生成该版本记录的活跃事务
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇Activiti 7 启动流程实例 下一篇关于java的快速输入的一点小bug

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目