加入收藏 | 设为首页 | 会员中心 | 我要投稿 伊春站长网 (https://www.0458zz.com/)- 管理运维、图像技术、数据标注、智能营销、数据计算!
当前位置: 首页 > 站长学院 > MySql教程 > 正文

全网最全一篇MySQL数据库MVCC详解,不全你打我

发布时间:2022-08-09 11:20:32 所属栏目:MySql教程 来源:互联网
导读:什么是MVCC 全称Multi-Version Concurrency Control,即多版本并发控制,主要是为了提高数据库的并发性能。以下文章都是围绕InnoDB引擎来讲,因为myIsam不支持事务。 同一行数据平时发生读写请求时,会上锁阻塞
  什么是MVCC
  全称Multi-Version Concurrency Control,即多版本并发控制,主要是为了提高数据库的并发性能。以下文章都是围绕InnoDB引擎来讲,因为myIsam不支持事务。
 
  同一行数据平时发生读写请求时,会上锁阻塞住。但mvcc用更好的方式去处理读—写请求,做到在发生读—写请求冲突时不用加锁。
 
  这个读是指的快照读,而不是当前读,当前读是一种加锁操作,是悲观锁。
 
  那它到底是怎么做到读—写不用加锁的,快照读和当前读又是什么鬼,跟着你们的贴心老哥,继续往下看。
 
 
 
  当前读、快照读都是什么鬼
  什么是MySQL InnoDB下的当前读和快照读?
 
  当前读
  它读取的数据库记录,都是当前最新的版本,会对当前读取的数据进行加锁,防止其他事务修改数据。是悲观锁的一种操作。
 
  如下操作都是当前读:
 
  select lock in share mode (共享锁)
 
  select for update (排他锁)
 
  update (排他锁)
 
  insert (排他锁)
 
  delete (排他锁)
 
  串行化事务隔离级别
 
  快照读
  快照读的实现是基于多版本并发控制,即MVCC,既然是多版本,那么快照读读到的数据不一定是当前最新的数据,有可能是之前历史版本的数据。
 
  如下操作是快照读:
 
  不加锁的select操作(注:事务级别不是串行化)
  快照读与mvcc的关系
  MVCCC是“维持一个数据的多个版本,使读写操作没有冲突”的一个抽象概念。
 
  这个概念需要具体功能去实现,这个具体实现就是快照读。(具体实现下面讲)
 
  听完贴心老哥的讲解,是不是瞬间茅厕顿开。
 
 
 
  数据库并发场景
  读-读:不存在任何问题,也不需要并发控制
 
  读-写:有线程安全问题,可能会造成事务隔离性问题,可能遇到脏读,幻读,不可重复读
 
  写-写:有线程安全问题,可能会存在更新丢失问题,比如第一类更新丢失,第二类更新丢失
 
  MVCC解决并发哪些问题?
  mvcc用来解决读—写冲突的无锁并发控制,就是为事务分配单向增长的时间戳。为每个数据修改保存一个版本,版本与事务时间戳相关联。
 
  读操作只读取该事务开始前的数据库快照。
 
  解决问题如下:
 
  并发读-写时:可以做到读操作不阻塞写操作,同时写操作也不会阻塞读操作。
 
  解决脏读、幻读、不可重复读等事务隔离问题,但不能解决上面的写-写 更新丢失问题。
 
  因此有了下面提高并发性能的组合拳:
 
  MVCC + 悲观锁:MVCC解决读写冲突,悲观锁解决写写冲突
 
  MVCC + 乐观锁:MVCC解决读写冲突,乐观锁解决写写冲突
 
  MVCC的实现原理
  它的实现原理主要是版本链,undo日志 ,Read View 来实现的
 
  版本链
  我们数据库中的每行数据,除了我们肉眼看见的数据,还有几个隐藏字段,得开天眼才能看到。分别是db_trx_id、db_roll_pointer、db_row_id。
 
  db_trx_id
 
  6byte,最近修改(修改/插入)事务ID:记录创建这条记录/最后一次修改该记录的事务ID。
 
  db_roll_pointer(版本链关键)
 
  7byte,回滚指针,指向这条记录的上一个版本(存储于rollback segment里)
 
  db_row_id
 
  6byte,隐含的自增ID(隐藏主键),如果数据表没有主键,InnoDB会自动以db_row_id产生一个聚簇索引。
 
  实际还有一个删除flag隐藏字段, 记录被更新或删除并不代表真的删除,而是删除flag变了
 
 
 
  如上图,db_row_id是数据库默认为该行记录生成的唯一隐式主键,db_trx_id是当前操作该记录的事务ID,而db_roll_pointer是一个回滚指针,用于配合undo日志,指向上一个旧版本。
 
  每次对数据库记录进行改动,都会记录一条undo日志,每条undo日志也都有一个roll_pointer属性(INSERT操作对应的undo日志没有该属性,因为该记录并没有更早的版本),可以将这些undo日志都连起来,串成一个链表,所以现在的情况就像下图一样:
 
 
 
  对该记录每次更新后,都会将旧值放到一条undo日志中,就算是该记录的一个旧版本,随着更新次数的增多,所有的版本都会被roll_pointer属性连接成一个链表,我们把这个链表称之为版本链,版本链的头节点就是当前记录最新的值。另外,每个版本中还包含生成该版本时对应的事务id,这个信息很重要,在根据ReadView判断版本可见性的时候会用到。
 
  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线程统一清除

(编辑:伊春站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读