MVCC原理


JAVA后端开发知识总结(持续更新…)


MVCC与MySQL单表优化分页查询



一、ACID靠什么保证

  • 原子性由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql。
  • 一致性一般由代码层面来保证。
  • 隔离性由MVCC来保证。
  • 持久性由内存 + redo log来保证,MySQL修改数据的同时在内存和redo log记录这次操作,事务提交的时候通过redo log刷盘,宕机的时候可以从redo log实现安全恢复。

二、MVCC(多版本并发控制)

2.1 MVCC简述

  MVCC 全称是 multiversion concurrency control,即多版本并发控制,是 InnoDB实现事务并发与回滚的重要功能。

  • 《阿里数据库内核’2017/12’月报》

  指的是一种提高并发的技术。最早的数据库系统,只有读读之间可以并发,读写,写读,写写都要阻塞。引入多版本之后,只有写写之间相互阻塞,其他三种操作都可以并行,这样大幅度提高了InnoDB的并发度。
  InnoDB是在undo log中实现的,通过undo log可以找回数据的历史版本。找回的数据历史版本可以提供给用户读,也可以在回滚的时候覆盖数据页上的数据。在InnoDB内部中,会记录一个全局的活跃读写事务数组,其主要用来判断事务的可见性。

对于MVCC

  1. 被MySQL中的事务型存储引擎 InnoDB 所支持。
  2. 可以认为,MVCC是行级锁的变种,应对高并发事务时,MVCC比单纯地加锁更高效。
  3. MVCC是InnoDB实现隔离级别的一种方式,用于实现读已提交可重复读两种隔离级别。
  4. undo log是 MVCC 事务特性的重要组成部分。
  5. InnoDB的MVCC是通过在每行记录后面保存三个隐藏的字段来实现的。可以使得大多数读操作都不用加锁。
  6. MVCC是乐观锁的一种实现,是通过保存数据在某个时间点的快照来实现的,写操作更新最新的版本,读操作读取旧版本。
  7. 对于读未提交,直接读取最新版本的数据。
  8. MVCC只实现读取已提交和可重复读,InnoDB在可重复读的隔离级别下,使用MVCC+Next-key lock解决幻读

2.2 undo log

  undo log 与 redo log 一起构成了 MySQL 事务日志,主要用于数据修改的回滚。事务中的每一次修改,InnoDB都会先记录对应的 undo log 记录。

  当 INSERT 一条记录时,undo log 中会记录一条对应的 DELETE 记录,反之亦然,当 UPDATE 一条记录时,它记录一条对应相反的 UPDATE 记录。

  在事务回滚时,可以从 undo log 中反向读取相应的内容,并进行回滚,也可以根据 undo log 中记录的日志读取到一条被修改后的数据的原值。

  • insert undo log

  事务在 INSERT 新记录时产生的undo log,只在事务回滚时需要, 并且在事务提交后就可以立即丢弃。这是因为 INSERT 操作在事务提交前只对当前事务可见,因此产生的Undo日志可以在事务提交后直接删除。

  • update undo log

  事务对记录进行delete和update操作时产生的undo log,不仅在事务回滚时需要,一致性读也需要,所以不能随便删除,只有当数据库所使用的快照中不涉及该日志记录,对应的回滚日志才会被删除。

2.3 InnoDB的MVCC新增的三个字段

  1. 事务ID(DB_TRX_ID):记录插入或更新该行的最近一个事务的事务标识符。
  2. 回滚指针(DB_ROLL_PTR):指向该行对应的 undo log 的指针。
  3. DB_ROW_ID:随着新行插入而单调递增的行ID,它就是 AUTO_INCREMENT的主键ID。

2.4 当前读与快照读及版本读取

  • 快照读
  1. 通过 undo log + 数据行获取到事务开启前的原始数据的过程。
  2. 简单的select操作。
  3. RR级别下,快照读是通过MVVC(多版本控制)和undo log来实现的。

  每当一个事务更新一条数据时,都会在写入对应 undo log 后将这行记录的隐藏字段 DB_TRX_ID 更新为当前事务的事务 ID,用来表明最新更近该数据的事务是当前事务。此时,如果另一个事务SELECT数据时,读到该行的 DB_TRX_ID 不为空且 DB_TRX_ID 与当前事务的事务 ID 不同,就说明这一行数据是另一个事务修改并提交的。此时需要进行版本读取操作。

简版的演示事务对某行记录的更新过程(来源于《MySQL-InnoDB-MVCC多版本并发控制》):

在这里插入图片描述

  • 版本读取(可见性比较算法)——判断undo log中哪个版本对当前事务可见
  1. 使用ReadView(快照),ReadView是一个包含当前已经开始但是没有提交的事务的列表,记录每个事务的事务id,记最小事务id为min_id,最大事务id为max_id。
  2. 如果记录当前版本的trx_id小于min_id,说明这个记录版本是已经被提交过的,对其它事务可见。
  3. 如果记录版本的trx_id大于max_id,说明这个记录版本是ReadView生成之后发生的,不能访问。
  4. 如果记录版本的trx_id在min_id和max_id之间,需要判断trx_id是否在ReadView中
  •   如果在ReadView中,说明事务还未提交,该记录版本不可访问;
  •   如果不在ReadView中,说明该事务已经提交,该记录版本可以访问。
  • 如果当前记录版本不可读,就根据回滚指针DB_ROLL_PTR找到旧版本的记录再进行判断。
  • 对于读已提交,每次查询都会生成一个新的ReadView。
  • 对于可重复度,一个事务只在第一次SELECT的时候生成一个ReadView,之后的查询复用这个ReadView。

实例

  1. 假设当前有一个事务id为2的事务A,修改一个事务id为1(已提交)的某一行的a字段,产生一个版本快照。
  2. 假设事务A还没有提交,此时事务B第一次对该行进行SELECT,事务id为3,此时生成ReadView为[2, 3],根据版本读取规则,先找到trx_id为2的记录版本,发现不可读,于是通过回滚指针找到trx_id为1的记录,读取成功。
  3. 当事务A提交之后,事务B再次对该行进行SELECT查询,在读已提交和可重复读两种隔离级别下有不同的情况:
  •   如果是读已提交,则会创建一个新的ReadView为[3],此时读取trx_id为2的记录成功,也就是读取到了在事务期间提交的数据。
  •   如果是可重复读,则会使用第一次查询时的ReadView为[2, 3],此时读取的是trx_id为1的记录,从而实现了可重复度。
  • 当前读
  1. 在读取数据库时,需要读取的是行的当前数据,而不需要通过 undo log 回溯到事务开启前的数据状态。
  2. select … lock in share mode
  3. select … for update
  4. insert、update、delete
  5. RR级别下,当前读是通过record lock和gap lock来实现的。

三、MySQL单表优化分页查询

更新中。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值