Checkpoint 定义
抛开官网定义从咱们对数据库系统了解来看,批改数据个别是在缓存进行批改,数据库会有专用后盾 Backend 过程负责定时将脏块刷入磁盘,进行一个长久化。PG 的 Checkpoint 也是相似,官网文档对 Checkpoint 的定义,首先 Checkpoint 是一个程序的事物记录点,同 Checkpoint 这个工夫之前所有的 heap,以及索引文件批改都被认为是无效的。
在 Checkpoint 执行时,所有脏数据会被刷到磁盘,并记录一个非凡的切换记录到 Wal 日志里,当然这个数据批改是在 WAL 日志之后,也就是说是日志后行,也叫预写式日志。Checkpoint 还有一个作用,在 Crash 后或者重启须要 REDO 时,会依据 Checkpoint 记录去复原到对应工夫点。
这是 Checkpoint 作用的一个体现。从其它信息能够看到在 Checkpoint 之后,这时会把所有的数据做长久化到磁盘上,同时还会清理一些数据库认为不再须要的 Wal 日志,而在运维过程中要特地留神这个点,因为某些异样会导致 wal 累积;wal 的清理是一个清理加回收的机制。
简略来说 Checkpoint 是事务程序记录点,次要进行刷脏页,redo 时会参考 Checkpoint 进行日志回放,此外还会清理不须要的 Wal 日志。
Checkpoint 触发条件
接下来看 Checkpoint 触发条件。在 PostgreSQL 中 Checkpoint 是由 Checkpointer 过程来执行的。Checkpoint 过程的主流程是无条件 for 循环,在未触发 Checkpoint 时,始终在 Waitlatch 中 Sleep,也就是在 Epoll_wait 中察看 List 链表,看是否有事件句柄曾经就绪。
就绪就是某个条件是否在触发 Checkpoint,如果已存在一个就绪事件,那么 Wake up,通过 Setlatch 中 Write pipe 形式去进行唤醒并执行 Checkpoint。能够看到左图就是 Checkpoint 触发形式第一种被动定时触发,右图是另外一种形式其它过程被动触发。
这里次要看被其它过程触发的状况,被动触发这里能够留神到其实也是过程间通信一个比拟好的范例。
这些其它过程例如 Postgre 能够用 Force 场景去触发;Startup 在 End of recovery 也就是 REDO 完结时去触发;Walwriter 在 XlogFlush 时,如果接管 Shutdown 信号也会去做 Checkpoint;应用 Basebackup 时也会去做一个 Checkpoint。这些 Backend 或工具,会通过 RequestCheckpoint 函数去触发 Checkpoint。
咱们看一下具体的步骤是什么样的:
第一步,各自过程在各自触发场景里先批改共享内存 Ckpt_flags,Checkpointer 过程后续会依据 ckpt_flags 去决定是否要触发 Checkpoint。
第二步各个过程通过调用零碎 kill 接口,向 Checkpointer 过程发送 INT 中断信号。
这里为什么要发信号?Checkpoint 在闲暇时是在 Epoll_wait 中 Sleep 的,在这里须要触发让它 wakeup。Checkpointer 主函数注册了中断信号的信号处理函数,当收到中断信号后会进入中断,调入处理函数去执行 SetLatch,以 Write local pipe 形式去进行 Wakeup。唤醒后 Checkpoint 会去检测 Ckpt_flag 被设置为什么内容,如果被设置则会执行 Checkpoint。
来看下能够触发 checkpoint 的 Flags:
第一个是 checkpoint is shutdown,数据库 shutdown 的时候
第二个是 End of recovery,当 REDO 完结时,
第三个 Immediate 立刻执行
能够看到这些 Flag 其实是多个 Flag 进行一个或运算,组成了 ckpt_flags。
接下来两个绝对于其它 Flag 是一个辅助 Flag,Wait 指的是触发后要期待 Checkpoint 实现,Requested Flag 示意曾经触发了 checkpoint。
次要看 Cause_Xlog 和 Cause_Time。Cause_Xlog 指数据库在有大量数据写入时,生成的 Wal 日志达到肯定数量触发,其实就和之前比拟相熟的参数 Checkpoint_segments 相似。
当 Wal 日志新增的数量大于等于 Checkpoint_segments -1 时会触发 Checkpoint,如果是默认参数配置,例如 Walsize 16M 而后 Max_Wal_size 为 1G,这种状况下它是 42 个 Wal 日志会触发一次 Checkpoint
咱们有时会看到 Checkpoint 的比拟频繁,它会提醒须要去 Increase Max_Wal_size 参数。那么为什么要增大这个参数?Checkpoint_segments 参数在 9.5 之后就不再是独自的参数,它是 Max_wal_size 和 CheckPointCompletionTarget 两个参数联动的后果。进步 Max_Wal_size,相当于减少了被除数,那就是说要生成更多的 Wal 日志时,才会去做 Checkpoint,升高了 Checkpoint 的频率。
Cause_Time 其实很简略,就是 Checkpoint timeout,默认每到五分钟主动触发一次。
以手动强制触发 Checkpoint 为例能够看到当语句是 Checkpoint_statments 时,通过 Request Checkpoint 函数接口,理论传递的几个 Flag 做了一个运算,如果 RecoverInProgress() 为 false 则为 CHECKPOINT_FORCE。这里提及下在主库里是 checkpoint,在备库里是 restartpoint
Checkpoint 会做什么
后面定义时曾经聊到这一块,当然 Checkpoint 首先要做的必定是刷脏页,具体的过程是怎么样的?须要刷哪些内容?这里有个图表,从上向下顺次能够看到 CLOG 或者子事务,组合事务还关系映射、复制槽、快照等等。
抓取 Checkpointer 的零碎调用,抓取过程中 Checkpoint flush 的对象不同,具体的操作可能不同。
次要看下数据缓冲的 Flush 过程,首先用 Open 关上文件句柄是 Relationfilenode 对应的物理文件。这里是用 Pwrite 接口写入批改内容,Pwrite 接口设计思维是为了解决并发下原子写问题。
lseek + write 的形式无奈保障原子性。
例如并行写入的场景下,线程 A 不再指向文件的真正开端地位,它指向文件先前的默认地位,线程 B 写入信息的地位。当线程 A 写入文件时,可能被线程 B 写入的信息笼罩。
插入 PPT 图片
所以前面有了 Pwrite 接口,看起来它是一个原子的写入接口,实际上在数据库里,并不是原子写,咱们都晓得 DB 的 BLCLKSZ 是 8k,OS 是 4K,如果写一半异样掉电是不是会产生坏页?咱们称为页裂场景。
在这里 PG 有 FPW 这个个性去进行兜底。假如产生了异样掉电,只写入了 4k,那么就能够通过残缺的程序 Wal 日志去进行复原。
Pwrite 写完后,调用 Fsync 将缓冲立刻落盘。
这里为什么要用 Fsync 去做缓冲区刷入磁盘立刻落盘的操作呢?通常写文件时内核是先将数据复制到缓冲区再排入写队列,晚些时候再写入磁盘,这种形式称为提早写。为了保障文件系统和缓冲区内容的一致性,确保批改过块立刻写到磁盘上,能够应用 Fsync 将对应的缓冲快立刻落盘。除了 Fsync 还有其它两种形式,比方 sync,
sync 的特点是将所有批改过的缓冲块排入写队列就间接返回。而 Fsync 它只对由 FD 指定文件起作用,同时它要期待写磁盘操作完结才会返回。
另外一种落盘形式叫做 fdatasync,在 PG 里也应用到了,很多文件也是用 Fsync 去进行落盘操作,那么有什么区别?它和 Fsync 的性能大抵上雷同,然而它只影响文件中数据局部,除数据外,Fsync 可能还会同步更新文件属性。
上面介绍更新位点信息,例如下图是更新的 ControlFile 位点信息,更新 ControlFile 的共享内存构造体,再把 ControlFile 长久化到 pg_control 文件里去,除此之外,还有 XlogControl 这些共享内存构造体等。
插入 PPT 图片
这些点有什么用?Checkpoint 这些点后面定义的时候也理解到,当 REDO 时,会依据这些点去进行回放,这是其中一个性能。在这里还有一个须要留神的中央,以这两个共享内存构造体为例,一个是 ControlFile,一个是 Xlogctl。在应用、批改时加的锁是不一样的。
对于 ControlFile,它应用的是轻量级锁 LWLock。为什么要用 LWLock?咱们晓得在 PG 里 LWLock 自身就是用来爱护共享内存并发拜访的机制,它实用并发拜访临界区较长的场景。在提供爱护同时,等锁过程会进入一个专用队列进行 Sleep,能够升高一些性能上损耗。XlogCtl 这个共享内存构造体,在这里用的是自旋锁 SpinLock,SpinLock 大多数状况下是一种 TAS 的操作,如果获取到就执行,没有获取到下次会再尝试获取。
因为临界区非常简单只有一条一行,因而用自旋锁效率就足够,然而临界区比拟长就不太实用了,这样可能会造成 CPU 空转。
第三个点比拟重要的是清理老旧 Wal 日志,PG 目录下保留多少日志就取决于这里。
运维过程中常常会遇到 Wal 日志累积的场景,咱们先看 Wal 日志是是怎么的逻辑。首先会依据指针的偏移量,去估算出两次 Checkpoint 之间产生的 Wal 日志量。在 KeepLogSegments 这个函数里,依据配置的 Wal_keep_segments 及最小复制槽的 Restart_lsn 获得这两个之间最小的做为一个取值,计算出一个日志号。
比这个日志号更早的日志,后续会进入清理和回收逻辑,也就是复制槽的 Restart_Isn 和 Wal_keep_segments,这两个值会影响到 Wal 日志保留的计算。
这里根据上次计算的偏移量,联合一套计算公式,计算出须要重用开始回收日志号,它会从这个日志号进行回收重用,也就是两次 Checkpoint 之间做预估,回收一部分后续应用。
插入 PPT 图片
Checkpoint skipped 机制
首先关注触下 flags,当 flags 为 checkpoint-is-shutdown 或者 checkpoint-end-of-recovery 或者 checkpoint-force 时,也就是说数据库停机,redo 实现,手动强制执行触发 checkpoint 这几种形式是不是进入这个 if 逻辑的
接下来看第二个 If 条件,当 Important_lsn 和 ControlFile 记录的 Checkpoint 点相等时它会进入到外面间接 Return。那么这个等值成立的条件是什么?右值咱们很相熟,是上一次 Checkpoint 记录的点。左值是什么?是 Wal 日志正在写入数据的地位。以后 Wal 日志写入地位等于上次 Checkpoint 的地位。这阐明它的位点和 Wal 日志没有更新,也就阐明了咱们数据库里没有批改数据的操作,也就没有新的脏块,在这里跳过刷脏、清理 WAL 等操作逻辑成立的。
这个机制看起来比拟正当,然而实际上某些场景会不会存在隐患?上面是之前解决的一个案例,简略形容一下,有一个实例 Wal_keep_segments 配置为 256,并且没有应用复制槽,同时归档也没有报错,然而在这种状况下,它保留了 17000 多个 Wal 日志。
起初判断是不是 Checkpoint 或过程异样,但在抓取零碎调用之后,发现并没有异样。当关上 Debug 日志之后,能够看到它是进入了方才剖析的 Checkpoint skip 机制,这个案例场景是集中继续一段时间的大并发写入,Wal 日志产生速度十分快,同时对应归档速度稍慢。这样 Wal 产生快、归档慢,自身是迟缓的累积的过程。后续写入动作就进行了,并且在一段时间内没有批改数据的操作,这时 Checkpointer 每到 5 分钟通过 Timeout 主动触发时,就进入 Checkpoint skip 机制,不会再去清理 wa 日志。
插入 PPT 图片
这里的躲避计划是手动强制触发了一次 Checkpoint,从数量统计下来曾经确定胜利了,就只剩 300 多个了。
这个是一种比拟非凡的场景,然而咱们有可能会遇到,这里其实还有一个小点,咱们方才曾经理解了清理 Wal 日志的逻辑,那 Wal 日志累积的场景还会有哪几种?其实还有以下几种。
插入 PPT 图片
一、Wal_keep_segments 被调大,13 之后改为 Wal_keep size,这个参数给调大。
二、复制槽长时间处于非沉闷状态,能够查问 PG Replication slow 视图,是否有 Inactive 的复制槽,当然在 13 版本后减少了一个参数能够缓解这个问题,也就是 max_slot_wal_keep_size,这个参数能够配置复制槽保留多少 Wal 日志。
三、可能是 archive 过程异样,在过程异样的状况下,是不会进入 Remove 函数去清理 Wal 日志。
四、Checkpointer 过程的其它异样,可能性比拟小。
Checkpoint 过程记录
Checkpoint 的过程记录能够通过设置 GUC 参数 Log_ Checkpoints=on,关上这个参数 pgLog 会记录过程,记录两条信息,第一条是 Checkpoint 的触发条件和形式,第二条是 Checkpoint 的工作内容,比如说刷了多少脏块,清理或回收多少 Wal 日志,执行工夫多久等。
插入 PPT 图片
第二种形式能够通过命令行工具 pg_controldata 去解析 pg_control 文件,这样能够获取 Checkpoint 相干位点信息。同样还能够应用 pg_control_Checkpoint 函数,去获取 Checkpoint 相干位点。这个比拟适宜做 Checkpoint 相干监控。这里能够看到命令函数工具和零碎函数,它的位点和这几个我的项目或条目是统一的,在内核里拜访的是雷同构造。