前言
尝试理解MySQL如何解决数据丢失问题。
前置背景
程序进行网络IO操作是,都要考虑连接断开问题;在进行文件IO时,则需要考虑主机宕机和断掉可能引起的数据丢失问题。这是事务ACID中的D,持久性要求。
MySQL 的实例是以页为操作单位的,页是可以回写磁盘的。设想两种情况:
- 假如每次写页都需要刷新到磁盘,则数据库的性能就会大大地降低
- 刷新的过程中假如断电了,数据就丢失来。
为了解决上述两个问题,事务数据库系统一般会采用 Write Ahead Log (WAL) 策略,即当前事务提交时,先写重做日志 redo log,再修改页。那样就可以在掉电的时候,通过重做日志恢复页的数据。(假如重做日志都没写就失败,则可以认为事务已经失败?)
为什么有了记录数据变动的日志,还需要页和文件呢?MySQL 不能像 Redis 的 AOF 那样保持数据和性能?
要满足问题里的情况,必须要满足以下两个条件
- 缓冲池可以缓存这个数据库
- 重做日志 redo log 可以无限增大
这两个条件基本上就是 Redis 的设定来,但是 MySQL 的数据量一般会比 Redis 的大得多,有TB 级别的存储需求,而且数据结构和查询需求也复杂,同量的数据,MySQL 的存储会更大。所以条件一难以成立。
重做日志 redo log 可以无限增加,这对运维的要求是比较高的,而且记录redo log,会比 MySQL 存储的数据本身要大得多。大量的数据需要恢复时,参考 Redis AOF 的缺点,MySQL 的恢复时间是极长的。这很不划算。
Checkpoint 技术
解决的问题
- 缩短数据库恢复时间
- 缓冲池不够用的时候,把脏页刷新到磁盘中
- redo log 不可用时,刷新脏页
InnoDB 使用Checkpoint
InnoDB 有两种Checkpoint
- Sharp Checkpoint 只会发生在数据库关闭时,把所有的脏页都刷新到磁盘中
- Fuzzy Checkpoint 工作过程中刷新,包括但不仅包括以下情况:
- Master Thread Checkpoint
- FLUSH_LRU_LIST Checkpoint 为了保证 LRU 列表有一定的可用空闲页,以前是通过阻塞用户线程进行的,现在由 Page Cleaner 线程进行。
- Async/Sync Flush Checkpoint 同上,这个是保证 redo log 的可用性。
- Dirty Page too much Checkpoint 这个比较简单,当缓冲池中脏页数量达到90%(MySQL8 默认),数据库则进行Checkpoint
参数查看
- LSN (log Sequence Number) 标记版本(如下图: 目前最新的版本是:33144255018,而Last Checkpoint at 33144254628,假如触发了checkpoint,就会追赶最新的版本了)
show engine innodb status\G;
...省略其他信息
---
LOG
---
Log sequence number 33144255018
Log buffer assigned up to 33144255018
Log buffer completed up to 33144255018
Log written up to 33144255018
Log flushed up to 33144255018
Added dirty pages up to 33144255018
Pages flushed up to 33144254628
Last checkpoint at 33144254628
39941493 log i/o's done, 0.43 log i/o's/second
参考
- 《MySQL 技术内幕 InnoDB存储引擎》 姜承尧 第二版 2013
后记
这是深入理解MySQL的第二篇。整体来说,是顺着高性能和高可用存储,Checkpoint技术是必不可少的,其实质就是缓存数据和磁盘数据何时应该同步的一种具体实现,Checkpoint技术中有不少参数可以调整,通过 show engine innodb status\G; 可以观察到引擎的整体工况。