技术分享 | show engine innodb status中Pages flushed up to 的含义

本文详细解析了 MySQL InnoDB 存储引擎中的 LSN(日志序列号)机制,重点阐述了 Pages flushed up to 和 Last checkpoint at 的含义及二者之间的差异。通过测试验证了在不同场景下这些 LSN 相关指标的变化规律。

作者:胡呈清

爱可生 DBA 团队成员,擅长故障分析、性能优化,个人博客:https://www.jianshu.com/u/a95ec11f67a8,欢迎讨论。

本文来源:原创投稿

*爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。


有一个很久前就存在的疑惑:
在没有写入的情况下,show engine innodb status 中的 Pages flushed up to 为什么不等于 Last checkpoint point?它表示什么?

今天在写一篇文章时,想通过一个测试,从这几个 LSN 的变化来验证一个说法,结果重新勾起了这个问题,在一番“研究”后(其实就是google了一通,当然还是需要一些测试验证),终于弄明白了其中含义。

主要还是借鉴了一篇文章:http://blog.itpub.net/30221425/viewspace-2154670/,因为有大量的源码分析,而我看不懂源码,所以花了不少时间才理解其中原理,并且通过测试验证了其准确性,然后整理成了一个让像我这样不懂代码的人更方便阅读的文档。

今天在写一篇文章时,想通过一个测试,从这几个 LSN 的变化来验证一个说法,结果重新勾起了这个问题,这次我决定彻底弄明白到底是怎么一回事。

主要还是借鉴了一篇文章:http://blog.itpub.net/30221425/viewspace-2154670/,因为有大量的源码分析,而我也看不懂源码,所以花了不少时间才理解其中原理,并且通过测试验证了其准确性,然后整理成了一个更方便让像我这样不懂代码的人理解的文档。

LSN

show engine innodb status 的输出中,有一部分是 LSN 的状态:

mysql> pager grep -A 5 LOG                                      
PAGER set to 'grep -A 5 LOG'
mysql> show engine innodb status\G
LOG
---
Log sequence number 2471197058
Log flushed up to   2471197058
Pages flushed up to 2471197058
Last checkpoint at  2471197049
1 row in set (0.00 sec)

Log sequence number:所有修改数据的操作都会产生 redo log,这是系统当前 redo log 序列号(后面简称 LSN)的最大值;

Log flushed up to:当前已经刷盘的 redo log 的序列号;

Pages flushed up to:讨论的重点;

Last checkpoint at:最后一次 checkpoint 的位置。

Pages flushed up to 到底表示什么?最常见的说法就是脏页刷新到磁盘的 LSN,但 Last checkpoint at 也表示在此之前的数据页已经刷盘,而且通常我们看到的 Pages flushed up to 总是比 Last checkpoint at 大 ,所以这个说法肯定是错误的。

根据参考文章中的源码分析,Pages flushed up to 的取值逻辑是:

Pages flushed up to 取的是所有 buffer pool instance 中的 flush list 中最大 oldest modification lsn 页中的带有最小的 oldest modification lsn 的值(这里可能有点难以理解,我们可以简化理解为取 flush list 中最大的 oldest modification lsn);

如果取到的 oldest modification lsn 为0,意味着没有脏页,那么我们就取 log_sys->lsn 的值,即 show engineinnodb status 显示的 Log sequence number。

LSN 更新的逻辑

取 Pages flushed up to 的目的是什么?因为是取的 flush list 中脏页的最大 LSN,接下来做 checkpoint 时,可以用作这次刷脏的一个结束位置。

在一个正常运行的 MySQL 中,系统 LSN 随着 SQL 的执行(修改数据的SQL)不断增大(实时的),Pages flushed up to 值也会按某个频率获取更新,所以 Pages flushed up to 一般会落后于系统 LSN。然后 InnoDB master thread 每秒和每十秒做 checkpoint 时(还有其他的机制触发 checkpoint,这里不考虑并不影响理解),在将脏页刷盘后会将 Last checkpoint at 的值更新成 Pages flushed up to,由于 Pages flushed up to 在不断更新,所以我们观察到的 Last checkpoint at 一般会小于 Pages flushed up to。

在没数据写入的情况下,为什么 Last checkpoint point 不等于 Pages flushed up to?

是因为做 checkpoint 时同时 redo 日志会写 MLOG_CHECKPOINT,而 MLOG_CHECKPOINT 占用9个字节,所以系统 LSN 会加9,而由于脏页都被刷盘了,flush list 为空,获取 Pages flushed up to 时会直接取系统 LSN 值,所以也会比 Last checkpoint point 大 9。

获取 Pages flushed up to 和 checkpoint 不是一个原子操作,它是在 checkpoint 前就获取了,指的是下一次 checkpoint 结束的位置。

测试验证

我们手工开启一个事务写入一行数据(不提交),会发现如下图所示:

  • Log sequence number 增加了,因为系统 LSN 是实时产生的;

  • Log flushed up to 没变,说明 redo log 还没刷盘(也可能观察到这个值增加并且和 Log sequence number 一致,因为 InnoDB 后台线程每秒会刷 redo log,未提交事务的 redo log 可能提前刷盘);

  • Pages flushed up to 没变,因为这个值不是实时产生的,只要我们手速够快,在执行完 insert 后立刻查看 LSN,就可以看到这个值不变;

  • Last checkpoint point 没变,同样的只要手速够快,在 checkpoint 前查看到的值就不会变。

我们隔几秒钟后再查看 LSN,会发现:所有值都变了,并且前 3个值一样,Last checkpoint point 比它们的值小 9。这是因为InnoDB Master Thread 每秒、每十秒都会做 checkpoint,所有脏页都刷盘后,flush list 为空,符合我们前面所说的原理:

在持续写入的情况下,是可以观察到 Last checkpoint point 等于 Pages flushed up to 的(checkpoint 后还没有获取新的 Pages flushed up to):

<think>我们正在处理一个具体的MySQL InnoDB错误:'page_cleaner: 1000ms intended loop took 4064ms. The settings might not be optimal. (flushed=3 and evicted=0, during the time.)' 根据引用内容,特别是引用[3]指出,该错误通常是由于innodb_buffer_pool_size设置不够大导致的。此外,引用[4]和[5]也展示了类似的错误信息,并且引用[3]明确给出了解决方案:加大innodb_buffer_pool_size。 但是,我们还需要考虑其他可能的因素和优化措施。错误信息中提到的page_cleaner线程是InnoDB用于刷新脏页的后台线程,负责将缓冲池中的修改写回磁盘。如果这个线程无法及时完成工作,就会导致这个警告。 解决方案可以从以下几个方面考虑: 1. 增加innodb_buffer_pool_size:这是最直接的解决方案。缓冲池越大,能容纳的脏页就越多,page_cleaner线程就不需要频繁地刷新,从而减少长时间循环的风险。 2. 调整innodb_buffer_pool_instances:如果缓冲池很大(比如超过8GB),可以将其分成多个实例,以提高并发性。 3. 调整innodb_lru_scan_depth:这个参数控制每个缓冲池实例的页面清理器扫描LRU列表的深度。降低这个值可以减轻page_cleaner线程的负担,但可能会影响缓冲池的效率。 4. 调整innodb_io_capacity:这个参数定义了InnoDB后台任务(如刷新脏页)可用的I/O操作数。如果磁盘性能较好(如SSD),可以适当提高这个值。 5. 检查磁盘I/O性能:如果磁盘I/O本身很慢,那么即使调整参数也可能无济于事。需要确保磁盘能够处理当前的I/O负载。 根据具体的错误信息,我们看到flushed=3 and evicted=0,说明在这一次循环中只刷新了3个页面,而且没有页面被驱逐(evicted=0)。这通常意味着缓冲池的刷新效率很低,可能是因为缓冲池设置过小,或者I/O能力不足。 步骤建议: 1. 首先,评估当前innodb_buffer_pool_size的设置。通常建议设置为系统可用内存的50%-70%(但不要过大,以免导致系统交换)。可以通过以下命令查看当前设置: ```sql SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; ``` 2. 如果当前设置较小,可以逐步增加(每次增加原值的20%-50%),然后观察错误是否减少或消失。修改方法是在my.cnf(或my.ini)配置文件中设置: ``` innodb_buffer_pool_size = XG # X为具体的GB数 ``` 3. 如果缓冲池较大(比如超过8GB),考虑设置innodb_buffer_pool_instances。建议每个实例至少1GB,通常设置为8-16个实例。例如: ``` innodb_buffer_pool_instances = 8 ``` 4. 调整innodb_lru_scan_depth:默认值为1024。如果问题依然存在,可以尝试降低这个值(比如降到256或512): ``` innodb_lru_scan_depth = 256 ``` 5. 调整innodb_io_capacity:根据磁盘性能调整。如果是普通机械硬盘,可以设置为200-400;如果是SSD,可以设置为2000或更高。例如: ``` innodb_io_capacity = 2000 ``` 6. 监控磁盘I/O负载,确保没有其他因素导致I/O瓶颈。 注意:修改配置后需要重启MySQL服务(部分变量支持动态设置,但innodb_buffer_pool_size和innodb_buffer_pool_instances需要重启)。 另外,引用[5]中出现了“Aborted connection”错误,这可能是由于网络问题或客户端超时导致,但和page_cleaner错误没有直接关联。如果也存在这样的连接错误,需要单独检查网络和超时设置。 总结:针对当前错误,优先增加innodb_buffer_pool_size,并根据系统情况调整其他相关参数。 下面按照要求,生成相关问题。 </think>### InnoDB Page Cleaner 错误解决方案 出现 `page_cleaner: 1000ms intended loop took 4064ms` 警告表示 InnoDB 后台线程刷新脏页(Dirty Page)时严重超时,可能导致 I/O 阻塞和性能下降。以下是详细解决方案: --- ### **一、核心问题诊断** - **错误本质**:Page Cleaner 线程预期在 1 秒内完成刷新操作,但实际耗时 4064ms(约 4 秒)。 - **关键参数**:`flushed=3` 表示仅刷新 3 个页面,`evicted=0` 说明无页面淘汰,证明 **缓冲池刷新效率低下**[^3]。 - **根本原因**: 1. **缓冲池不足**:`innodb_buffer_pool_size` 过小,导致脏页堆积(如引用[3][^3]所述)。 2. **I/O 瓶颈**:磁盘速度无法满足刷新需求。 3. **配置不当**:`innodb_io_capacity` 或 `innodb_lru_scan_depth` 参数未适配硬件性能。 --- ### **二、优化步骤** #### **1. 扩大缓冲池(首要措施)** - **操作**:将 `innodb_buffer_pool_size` 增加至系统可用内存的 **70%~80%**(需保留内存给 OS 及其他进程)。 - **验证命令**: ```sql SHOW VARIABLES LIKE 'innodb_buffer_pool_size'; -- 查看当前值 ``` - **配置示例**(在 `my.cnf` 中修改): ```ini innodb_buffer_pool_size = 16G # 根据内存调整,如 16GB 内存设为 12G ``` #### **2. 优化 I/O 相关参数** | 参数 | 建议值 | 作用说明 | |-----------------------|----------------|------------------------------------------------------------------------| | `innodb_io_capacity` | **SSD: 2000+** <br> HDD: 400-800 | 提高后台线程 I/O 吞吐上限(SSD 必调)[^4] | | `innodb_lru_scan_depth` | **256~512** | 降低 LRU 扫描深度,减轻 Page Cleaner 负担(默认 1024 易超时)[^5] | | `innodb_flush_method` | `O_DIRECT` | 绕过 OS 缓存,减少双重缓冲(Linux 推荐) | #### **3. 检查磁盘性能** - 运行 **I/O 基准测试**: ```bash fio --name=randwrite --ioengine=libaio --rw=randwrite --bs=16k --numjobs=4 --size=1G --runtime=60 --time_based ``` - **达标参考**: - SSD:随机写 > 5000 IOPS - HDD:若 IOPS < 200,需升级硬件或迁移数据库。 #### **4. 监控与验证** - **观察指标**: ```sql SHOW ENGINE INNODB STATUS\G -- 查看 BUFFER POOL AND MEMORY 章节 ``` - **关键指标**: - `Pages flushed` 是否稳定上升 - `Pending writes: LRU` 是否归零 --- ### **三、高级调优(持续超时场景)** 1. **增加 Page Cleaner 线程**: ```ini innodb_page_cleaners = 4 # 建议等于 CPU 核心数 ``` 2. **调整刷新算法**: ```ini innodb_flush_neighbors = 0 # SSD 禁用相邻页刷新,减少 I/O 量 ``` 3. **限制写入突发**: ```ini innodb_adaptive_flushing = ON # 启用自适应刷新 innodb_max_dirty_pages_pct = 60 # 降低脏页比例阈值 ``` > **注意**:修改后需重启 MySQL 服务(部分参数支持动态设置:`SET GLOBAL innodb_io_capacity=2000;`)。 --- ### **故障树分析** ```mermaid graph TD A[Page Cleaner 超时] --> B{缓冲池不足?} B -->|| C[扩大 innodb_buffer_pool_size] B -->|| D{磁盘 I/O 慢?} D -->|| E[优化磁盘或迁移实例] D -->|| F[调整 innodb_io_capacity] F --> G[降低 innodb_lru_scan_depth] ``` --- ### **引用对照** | 优化措施 | 相关引用 | 效果验证 | |------------------------|----------|------------------------------| | 扩大缓冲池 | [^3][^4] | 超时频率显著降低 | | 提高 `innodb_io_capacity` | [^4][^5] | `flushed` 值稳定增加 | | 减少 LRU 扫描深度 | [^5] | CPU 占用下降,响应时间缩短 |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值