mysql checkpoint
checkpoint机制,主要干的事情就是把脏页刷新到磁盘。
数据库只能保证最终一致性。你在数据库运行的时候,磁盘上的数据和内存中的数据不一致太正常了。
另外checkpoint也能保证数据库在挂了,或者出事了之后,缩短他的恢复时间。这个很好理解,MySQL有redo和binlog去保证事务的持久性。所以即使你数据没有落盘,也没关系,他是有日志的。但是,如果没有checkpoint,你日志跑了1周,这时候你MySQL挂了,你重做日志需要多久。这个是很慢的。所以checkpoint能保证数据能有效地落盘,缩短recover时间。
而内存中这么多页,MySQL怎么知道哪些是脏页需要落盘,哪些不是脏页。这就引出了我们之前说的flush list。
首先,每个页有一个LSN,而MySQL整个实例,有一个全局的LSN,你可以把这个LSN理解成checkpoint,最后redo log里还有一个LSN。整个MySQL体系下,就这三个地方有LSN。
这么聊很难理解,我举一个例子。
page 1 被加载到bp里,初始的lsn是100,然后这个页被修改了,事务提交了。假设此时MySQL全局lsn是200,那么page 1的lsn被修改为200,并且写入redo log。
过了一段时间 page 2被加载到bp里,初始的lsn是110,然后这个页也被修改了,事务提交了。假设此时MySQL全局lsn是500,那么page 2的lsn被修改为500,并写入redo log。
然后,下一个时间,触发checkpoint。page 1作为最后一页被写入进磁盘了。page 2没有被写入。紧接着,mysql挂了。那么MySQL再次启动时,就会重放200-500之间的lsn。
而这个lsn究竟代表的是什么?
其实代表的是此次操作所产生的log字节数。
然后lsn你可以show engine innodb status\G查看,在log部分下
last checkpoint at就是当前最后一次进行checkpoint位置的lsn。
page flushed up to 就是当前最大或者说是最新的刷新到磁盘上的lsn。
log sequence number 表示当前内存中redo log buffer的lsn。
log flushed up to表示刷新到redo log上的lsn。
原则上last checkpoint at <= page flushed up to
比如说,我有一个页,lsn80,被加载到内存。此时全局lsn是100。这时候,这个页被修改了,产生了20字节的log。那么这个页lsn变成120,并且被放到flush list中。
然后这个页又被修改了,又多了20字节的log。此时lru list中这个页lsn是140。但是flush list中,这个页的lsn是不会变的。因为flush list的组织方式,是用的这个页第一次被加载进来的lsn来组织的。
所以这时候,第一个lsn是120,下面的三个lsn是140.
之前有提到innodb_buffer_page_lru这个表。其中有两个字段,NEWEST_MODIFICATION 和 OLDEST_MODIFICATION。这两个字段也是内存中真实存在的。这也就是之前为什么我说大家在线上环境要慎用这个表。这是要扫内存的。开销很大。
后者表示页第一次被更新时的lsn,对应这个例子是120,前者是内存中最新的lsn,对应这个例子是140。
整个flush list就是由OLDEST_MODIFICATION从小到大排序。刷脏页的时候,也是由OLDEST_MODIFICATION最小的开始。而checkpoint就是脏页列表中那个最小的OLDEST_MODIFICATION。
写入磁盘后,这一页的lsn也就变成140了。
也就是说last checkpoint at是120.
page flushed up to是140.
这时候数据库crash了。MySQL从120的lsn开始重做,然后发现这个页已经刷新到140了,跳过。更新下一个lsn,当前,这个例子是没有下一个lsn了。你可以想象在120和140之间有其他操作,但是没有落盘。
这部分很难,还是要多理解。
对了chencopint的值是要落盘的,在ib_logfile的第一个文件的前面两个字节。但这个值不是你刷了脏页就一定会落盘。因为lsn小一点没事的。你最多就是scan的时候多扫描一点。反正都落盘了,page的lsn大于mysql的lsn,他就直接跳过了。写checkpoint实际上是在master thread中,每一秒会更新一次。
接下来说一下checkpoint的类别。
官方文档明确的分为了两种。
fuzzy checkpoint 和sharp checkpoint。
前者刷新部分脏页,对系统影响小。
后者是刷新全部脏页,比如在shutdown时。
而fuzzy checkpoint的时机有以下:
page cleaner threads,每1s和10s固定时间刷新。
脏页比例过大(由参数innodb_max_dirty_pages_pct控制,默认75)
以上两种情况都是page cleaner threads主动从flush list中刷新脏页。
以下的情况则是从lru list中进行判断:
flush_lru_list checkpoint
比如,你现在free list中已经没有页了 ,但是现在你需要加载一个页。那么你就需要到lru list中,在old段,找到最后一个页,淘汰。
然而很不幸,这个页是脏页。这个时候就会进行刷新。
这时,就会由innodb_lru_scan_depth这个参数进行控制。就是发生如上情况,你需要淘汰最后一个页,但是最后一个页是脏页,那么MySQL会探测排在最后的innodb_lru_scan_depth个数的页。并将其中的脏页刷新。
async/sync flush checkpoint
未完待续
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?