MySQL数据库InnoDB存储引擎Log漫游(3)

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS DuckDB + QuickBI 企业套餐,8核32GB + QuickBI 专业版
RDS Agent(兼容OpenClaw),2核4GB
简介: MySQL数据库InnoDB存储引擎Log漫游(3)

0、导读

本文重点介绍了InnoDB的checkpoint和Buffer Pool管理

04 – Checkpoint

         理论上来说,如果MySQL数据库InnoDB存储引擎的buffer足够大,就不需要将数据本身持久化。将全部的redo log重新执行一遍就可以恢复所有的数据。但是随着时间的积累,Redo Log会变的很大很大。如果每次都从第一条记录开始恢复,恢复的过程就会很慢,从而无法被容忍。为了减少恢复的时间,就引入了Checkpoint机制。

 在了解checkpoint原理之前,先看两个名词:

- 脏页(dirty page)
 如果一个数据页在内存中修改了,但是还没有刷新到磁盘。这个数据页就称作脏页。

- 日志顺序号(Log Sequence Number)
 LSN是日志空间中每条日志的结束点,用字节偏移量来表示。在Checkpoint和恢复时使用。


- Checkpoint 原理
假设在某个时间点,所有的脏页都被刷新到了磁盘上.这个时间点之前的所有Redo Log就不需要重做了。系统记录下这个时间点时redo log的结尾位置作为checkpoint. 在进行恢复时,从这个checkpoint的位置开始即可。Checkpoint点之前的日志也就不再需要了,可以被清除掉。为了更好的利用日志空间,InnoDB并不会删除以前的Redo Log文件. InnoDB用几个Redo Log文件首尾相连,构建了一个环形缓存(circular buffer)的日志空间。


- 有了Checkpoint之后的Recovery

  A. 首先要定期的将Checkpoint写入磁盘中某个地方.

  B. 做Recovery时,从磁盘中读出Checkpoint.

  C. 根据Checkpoint中的LSN找到Redo Log相应的位置,开始执行Redo Log.


- Sharp Checkpoint
  对于繁忙的系统来说,很少会出现这样的的一个时间点。为了能创造出这样一个时间点,最简单的办法就是:

   A. 在某个时间开始停止一切更新操作

   B. 所有的脏页被刷新到磁盘

   C. 记录当前Redo Log的结尾位置到磁盘上.

   D. Checkpoint结束,继续更新操作。

image.png

Sharp Checkpoint


 这个方法称作Sharp Checkpoint,显然对于繁忙的系统, 这种方法是不合适的。能不能在checkpoint时不停止用户的操作呢?


- Fuzzy Checkpoint

 现在我们来看看,不停止更新操作的Checkpoint如何做:

  A. 选取当前的Redo Log结束位置作为checkpoint点。

  B. 将所有checkpoint点之前的脏页写入磁盘.

  C. 将checkpoint点的位置持久化到磁盘上.

  如下图所示,因为在刷脏页的同时用户还在更新数据,LSN1前的某个脏页在刷到持久存储之前就有可能会被LSN1之后的某个操作又给修改了。当刷脏页到磁盘时,LSN1后的部分操作(R1,R2对应的操作)就会被刷入磁盘。停止更新操作做checkpoint时(Sharp Checkpoint),持久存储中存储的数据是某个确切时间点的内存数据的快照。而不停止更新操作做checkpoint时,持久存储中存储的数据不是某个确切时间点的内存数据的快照。因此被称作Fuzzy Checkpoint.

image.png

Fuzzy Checkpoint

- 幂等(Idempotence)规则
  如上图所示,checkpoint 在LSN1位置,当checkpoint完成时R1,R2对应的修改也被刷到了持久存储。恢复时要从LSN1位置开始,包括R1, R2在内。虽然,R1,R2的数据已经被刷入持久存储中了,R1,R2两个Redo记录仍然会被重新执。重新执行后,数据还能正确吗?

  这就要求InnoDB的Redo Log要满足幂等规则。幂等规则要求无论redo log被重复执行了多少次,数据始终正确。

   - 物理日志天然满足幂等规则

   - 逻辑日志需要特殊处理才能支持幂等规则

   前面说过InnoDB的Redo Log是物理到页,页内是逻辑日志。因此需要特殊处理,才能满足幂等规则。


- 数据页的最新(最大)LSN
 为了满足幂等规则,InnoDB中每个数据页上都记录有一个LSN。每次更新数据页时,将LSN修改为当前操作的redo log的LSN。在恢复时,如果数据页的LSN大于等于当前redo log的LSN,则跳过此日志。


- 异步Checkpoint
  实现了幂等规则后,脏页就可以在任何时间,以任何顺序写入持久存储了。InnoDB的buffer pool有一套单独的机制来刷脏页。因此很多情况下checkpoint时,并不需要写脏页到存储。只是将所有脏页的最小的LSN记做checkpoint.这被称作“异步checkpoint"(刷脏页到持久存储)

 checkpoint的实现在log0log.c.
 log_checkpoint()实现异步checkpoint.


- 同步Checkpoint
 InnoDB的buffer pool通过LRU的算法来决定哪些脏页应该被写入持久存储。如果包含最小LSN的页面频繁的被更新,它就不会被刷到存储上。这样就可能导致checkpoint点很长一段时间无法前进,甚至导致日志空间被占满。这时就要按照LSN由小到大的顺序写一部分脏页到持久存储。这被称做"同步Checkpoint"(要刷脏页到持久存储).
 log_checkpoint_margin().
 log_calc_max_ages()用来计算,‘判断是否要执行同步checkpoint’用到的参数.


05 – 缓存池(Buffer Pool)
        学习到这里,我更倾向于说这是一个”Redo+Undo+Buffer”的模式。为了提搞IO性能,脏页缓存在buffer中,Redo log也要先缓存在内存中,doublewrite也有内存buffer.

         InnoDB实现了一套Buffer 机制,称作Buffer pool,将存储在文件中的数据以页为单位映射到内存中.


- Buffer Pool的页分类
 Buffer pool内的页分为三种:
 A. 未被使用的页(空白的buffer),没有映射到一个数据文件中页。
 B. 净页,映射到了一个数据文件页,而且没有被修改过。内容和数据文件的页一样。
 C. 脏页,映射到了一个数据文件页,并且数据被修改过。内容和数据文件的页不一样。


- Buffer Pool的LRU页表
 InnoDB维护了两个LRU链表。当空间不足时,用来决定哪些脏页应该被首先写入磁盘,哪些净页应该被释放掉。
 A. buffer_pool->LRU,普通LRU链表,记录所有数据缓冲页。
 B. buffer_pool->unzip_LRU,是压缩页(row_format=compressed)解压后数据缓冲页LRU链表。

  LRU链表中的页面按最近一次的访问的时间顺序排列,头部是最近一次被访问的页面,尾部是最早一次被访问的页面。无论是读还是写一个页面上的数据,都要先获取这个页面。因此可以在获取页面时,维护LRU链表.当获取一个页面后,将其放到LRU链表的头部即可。
 buf_page_get_gen()和buf_page_get_zip()用来获取一个页面,他们调用
 buf_unzip_LRU_add_block()和buf_page_set_accessed_make_young()来维护LRU链表。


- flush_list
 同步checkpoint时,需要根据数据页修改的先后顺序来将脏页写入持久存储。因此除了LRU链表,buffer pool中还有一个按脏页修改先后顺序排列的链表,叫flush_list.当需要同步checkpoint时,根据flush_list中页的顺序刷数据到持久存储。
 A. 一个页只在flush_list中出现1次,因为一个页面只需要写一次。
 B. 按页面最早一次被修改的顺序排列。


06 – Mini-Transaction(MTR)
      前面提到Redo Log将数据的操作细分到了页面级别。但是有些在多个页面上的操作是逻辑上不可分裂的。InnoDB中用Mini-Transaction来表示这些不可再细分的逻辑操作。


- MTR的一致性
 为了满足MTR的一致性,MTR做了如下的设计:
 A. MTR的所有日志被封装在一起,当MTR提交时一起写入redo log buffer.这样做有2个好处:
    * 减少并发MTR对redo log buffer 的竞争。
    * 连续的存储在一起,恢复时的处理过程更简单。
 B. InnoDB在redo log的层面,将一个MTR中的所有日志作为Redo log的最小单元。在恢复时,一个MTR中的所有日志必须是完整的才能进行恢复。


- MTR日志的封装
 为了在日志文件中区分不同的MTR,MTR将MLOG_SINGLE_REC_FLAG或MLOG_MULTI_REC_END写入redo log(mtr_log_reserve_and_write()).
 A. 如果MTR的日志中只有一行记录,在日志的开始处添加MLOG_SINGLE_REC_FLAG,表示MTR中只有一条记录。
 B. 如果MTR的日志中有多行记录,在日志的结尾处添加一个类型为MLOG_MULTI_REC_END的日志,代表MTR的日志到此结束.


- MTR的LSN
 A. 因为在将日志写入redo log buffer时,才能获得LSN。所以修改数据时,并没有修改页上的LSN。需要在MTR获得LSN后统一修改。
 B. 一个MTR只有一个LSN. 一个MTR内修改的所有页的LSN相同。这样checkpoint就不会出现在MTR的中间。
 C. 在获得LSN后,如果被MTR修改的脏页不在buffer pool的flush_list里,就会被添加进去。看mtr_memo_slot_note_modification()和buf_flush_note_modification().


- 页级锁
 MTR提交时才写日志到redo log的做法,决定了MTR要使用页级锁。
 A. 一个页面不能同时被多个活动的MTR修改。
 B. MTR中数据页的锁,直到MTR提交时(日志写入redo log buffer)后才释放。

     锁对象存储在mtr的memo中。调用mtr_s_lock和mtr_x_lock来加锁时,锁对象被保存到memo中。解锁在mtr_memo_slot_release()中完成。


- MTR的ROLLBACK
 看完MTR的代码发现mtr没有记录undo日志,也不能rollback. MTR都是很小的操作单元,而且每个MTR都有明确的操作目标,因此比较容易保证其正确性。
 A. 因为页面操作是在内存中完成,并且页面有固定的格式,因此很多的页面操作是不会失败的。InnoDB存储引擎中的很多写页面的函数都没有返回值.
 B. 在对任何页面操作前,先要检查是否可能发生错误。如果可能发生错误就不能往下执行。如,当插入一行记录到B-Tree的节点时,首先检查页面有足够的空间。
 C. 使用更大粒度的锁(如B-Tree的锁),并且按照一定的顺序加锁。这样才能不导致死锁问题。以上是自己看代码后的大概印象,不一定说到了正点上。MTR模块的代码虽简单,但是MTR在其他模块大量的使用。要透彻的理解MTR,估计还得要看其他模块的代码,整理出来大部分MTR操作过程才行.


06 – 参考
 A. Database Systems: The Complete Book (2nd Edition)
 B. Transaction Processing: Concepts and Techniques
 C. how-innodb-performs-a-checkpoint
 D. InnoDB fuzzy checkpoints
 E. Heikki Tuuri Innodb answers – Part I
 F. Heikki Tuuri Innodb answers – Part II



相关实践学习
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
【涂鸦即艺术】基于云应用开发平台CAP部署AI实时生图绘板
相关文章
|
存储 SQL 关系型数据库
MySQL存储引擎简介
在选择相应的存储引擎时,需要充分考虑实际业务场景、性能需求和数据一致性要求,从而为数据管理提供最佳支持。
539 17
|
存储 缓存 关系型数据库
【MySQL进阶篇】存储引擎(MySQL体系结构、InnoDB、MyISAM、Memory区别及特点、存储引擎的选择方案)
MySQL的存储引擎是其核心组件之一,负责数据的存储、索引和检索。不同的存储引擎具有不同的功能和特性,可以根据业务需求 选择合适的引擎。本文详细介绍了MySQL体系结构、InnoDB、MyISAM、Memory区别及特点、存储引擎的选择方案。
2432 57
【MySQL进阶篇】存储引擎(MySQL体系结构、InnoDB、MyISAM、Memory区别及特点、存储引擎的选择方案)
|
SQL 数据库 数据安全/隐私保护
SQL Server数据库Owner导致事务复制log reader job无法启动的解决办法
【8月更文挑战第14天】解决SQL Server事务复制Log Reader作业因数据库所有者问题无法启动的方法:首先验证数据库所有者是否有效并具足够权限;若非,使用`ALTER AUTHORIZATION`更改为有效登录名。其次,确认Log Reader使用的登录名拥有读取事务日志所需的角色权限。还需检查复制配置是否准确无误,并验证Log Reader代理的连接信息及参数。重启SQL Server Agent服务或手动启动Log Reader作业亦可能解决问题。最后,审查SQL Server错误日志及Windows事件查看器以获取更多线索。
338 0
|
存储 关系型数据库 MySQL
MYSQL支持的存储引擎有哪些, 有什么区别
MYSQL存储引擎有很多, 常用的就二种 : MyISAM和InnerDB , 者两种存储引擎的区别 ; ● MyISAM支持256TB的数据存储 , InnerDB只支持64TB的数据存储 ● MyISAM 不支持事务 , InnerDB支持事务 ● MyISAM 不支持外键 , InnerDB支持外键
|
数据库
【YashanDB数据库】YAS-02079 archive log mode must be enabled when database is in replication mode
YAS-02079 archive log mode must be enabled when database is in replication mode
|
存储 关系型数据库 MySQL
MySQL存储引擎详述:InnoDB为何胜出?
MySQL 是最流行的开源关系型数据库之一,其存储引擎设计是其高效灵活的关键。InnoDB 作为默认存储引擎,支持事务、行级锁和外键约束,适用于高并发读写和数据完整性要求高的场景;而 MyISAM 不支持事务,适合读密集且对事务要求不高的应用。根据不同需求选择合适的存储引擎至关重要,官方推荐大多数场景使用 InnoDB。
833 7
|
存储 SQL 关系型数据库
MySQL存储引擎
本文介绍了数据库优化的多个方面,包括选择合适的存储引擎、字段定义原则、避免使用外键和触发器、大文件存储策略、表拆分及字段冗余处理等。强调了从业务层面进行优化的重要性,如通过活动设计减少外部接口调用,以及在高并发场景下的流量控制与预处理措施。文章还提供了具体的SQL优化技巧和表结构优化建议,旨在提高数据库性能和可维护性。
445 1
MySQL存储引擎
|
存储 缓存 关系型数据库
MySQL高级篇——存储引擎和索引
MyISAM:不支持外键和事务,表锁不适合高并发,只缓存索引,内存要求低,查询快MyISAM提供了大量的特性,包括全文索引、压缩、空间函数(GIS)等,但MyISAM不支持事务、行级锁、外键,有一个毫无疑问的缺陷就是崩溃后无法安全恢复。5.5之前默认的存储引擎优势是访问的速度快,对事务完整性没有要求或者以SELECT、INSERT为主的应用针对数据统计有额外的常数存储。故而 count(*) 的查询效率很高表名.frm 存储表结构;表名.MYD 存储数据 (MYData);
MySQL高级篇——存储引擎和索引
|
存储 关系型数据库 MySQL
数据库引擎之InnoDB存储引擎
【10月更文挑战第29天】InnoDB存储引擎以其强大的事务处理能力、高效的索引结构、灵活的锁机制和良好的性能优化特性,成为了MySQL中最受欢迎的存储引擎之一。在实际应用中,根据具体的业务需求和性能要求,合理地使用和优化InnoDB存储引擎,可以有效地提高数据库系统的性能和可靠性。
338 5
|
存储 缓存 关系型数据库
【赵渝强老师】MySQL的MyISAM存储引擎
在MySQL5.1版本之前,默认存储引擎为MyISAM。MyISAM管理非事务表,提供高速存储和检索,支持全文搜索。其特点包括不支持事务、表级锁定、读写互阻、仅缓存索引等。适用于读多、写少且对一致性要求不高的场景。示例代码展示了MyISAM存储引擎的基本操作。
213 1

热门文章

最新文章

推荐镜像

更多