当前位置: 首页 >> 技术文章 >> 从Oracle到PostgreSQL:一文掌握Checkpoint重要概念
从Oracle到PostgreSQL:一文掌握Checkpoint重要概念
发布时间:2019-08-13 发布人:钟浪峰 370


导读:Checkpoint是数据库中重要的概念,无论在Oracle,MySQL这个概念,它主要功能是在检查点时刻,脏数据全部刷新到磁盘,以实现数据的一致性和完整性。PostgreSQL为什么要设计Checkpoint呢?跟Oracle一样,其主要目的是缩短崩溃恢复时间。PostgreSQL在崩溃恢复时会以最近的Checkpoint为基础,不断应用这之后的WAL日志。下面我们就从Oracle的角度去学习下PostgreSQL的Checkpoint。


PG检查点进程


Oracle的checkpoint进程是由单独的ckpt进程负责的,PostgreSQL也一样,有专门的checkpoint进程checkpointer,它由postmaster负责创建。作为postmaster的子进程而存在,为几大重要的后台进程之一。

从下图中,可知postmaster进程号为1962。checkpoint的进程号为1965,其父进程号为1962,即为

PG检查点进程

Kill检查点进程可以如下图可以看到,其他进程也跟着checkoint进程重启了。有点类似于系统被初始化后。可见此进程对数据一致保护的重要性。

PG检查点进程


PG检查点的类型


在xlog.h文件中,有如下代码对checkpoint进行了相应的分类:  

/* These directly affect the behavior of CreateCheckPoint and subsidiaries */
#define CHECKPOINT_IS_SHUTDOWN  0x0001  /* Checkpoint is for shutdown */
#define CHECKPOINT_END_OF_RECOVERY      0x0002  /* Like shutdown checkpoint, but
                                                                                         * issued at end of WAL recovery */
 #define CHECKPOINT_IMMEDIATE    0x0004  /* Do it without delays */
#define CHECKPOINT_FORCE                0x0008  /* Force even if no activity */
#define CHECKPOINT_FLUSH_ALL    0x0010  /* Flush all pages, including those
                                                                                 * belonging to unlogged tables */
/* These are important to RequestCheckpoint */
#define CHECKPOINT_WAIT                 0x0020  /* Wait for completion */
/* These indicate the cause of a checkpoint request */
#define CHECKPOINT_CAUSE_XLOG   0x0040  /* XLOG consumption */
#define CHECKPOINT_CAUSE_TIME   0x0080  /* Elapsed time */          
                                                                            

  1. CHECKPOINT_IS_SHUTDOWN(shutdown检查点):在PG实例shutdown时做的检点

  2. CHECKPOINT_END_OF_RECOVERY(recovery end检查点): 在recovery 结束阶段做的检查点,类似于shutdown检查点,只不过在WAL恢复结束时发起。

  3. CHECKPOINT_IMMEDIATE(immediate检查点):不仅仅创建检查点,而且会马上做。这类请求一般在比较紧急的情况下,需要马上获取数据库一致状态的情况下。

  4. CHECKPOINT_FORCE(force检查点):即使没有xlog变更,也会做。请求这类检查点,往往只是想得到最近的checkpoint location而已。

  5. CHECKPOINT_FLUSH_ALL(flush检查点): 当发起flush 所有pages时发起,包括那些不logging的表

  6. CHECKPOINT_WAIT (wait检查点):检查点不会马上做,但会一直等待,直到检查点完成。往往比较重要的一些操作,但不是非常紧急的,可以请求该类检查点。尤其是一些DDL操作,对数据一致性要求高于响应时间。

  7. CHECKPOINT_CAUSE_XLOG(xlog检查点):由xlog的消耗引起,产生新xlog文件。

  8. CHECKPOINT_CAUSE_TIME (time检查点):由时间elapse引起。

从上可知,PostgreSQL的checkpoint类型也很丰富,但是它比oracle的类型少些,比如增量checkpoint.


Checkpoint触发条件


以下几种情况会触发数据库操作系统做检查点操作:

1) 超级用户(其他用户不可)执行checkpoint命令

2) 数据库shutdown

3) 数据库recovery完成

4) WAL日志量达到了触发checkpoint阈值

5) 周期性地进行checkpoint

6) 需要刷新所有脏页


checkpoint调优


如何设置调优checkpoint,设置正确的参数呢?首先我们要先了解PostgreSQL数据的同步机制。

PostgreSQL数据的同步机制

数据同步机制

图1. 数据同步机制


整个同步机制如上图1所示。数据库的后台进程在执行用户事务时,发生的数据更改是先写入缓冲池中,对应PostgreSQL就是shared buffers。PostgreSQL的缓冲池一般设置为总内存的1/4左右,缓冲池里面的这些数据更改,在事务提交时,是无需同步写入到磁盘的。因为在事务提交时,会先写入WAL日志,有了WAL日志,就可以在异常情况下将数据恢复,保障数据安全,因此数据本身是否在提交时写入磁盘就没那么重要了。PostgreSQL是只是在需要的时候,例如脏页较多时、或一定时间间隔后,才将数据写回磁盘。

脏页处理的过程分为几个步骤。首先是由background writer将shared buffers里面的被更改过的页面(即脏页),通过调用write写入操作系统page cache。在函数BgBufferSync可以看到,PostgreSQL的background writer进程,会根据LRU链表,扫描shared buffers(实际上是每次扫描一部分),如果发现脏页,就调用系统调用write。可以通过设置bgwriter_delay参数,来控制background writer每次扫描之间的时间间隔。background writer在对一个页面调用write后,会将该页面对应的文件(实际上是表的segement,每个表可能有多个segment,对应多个物理文件)记录到共享内存的数组CheckpointerShmem->requests中,调用顺序如下:

BackgroundWriterMain -> BgBufferSync -> SyncOneBuffer -> FlushBuffer -> smgrwrite->
mdwrite-> dirty_segment-> register-> ForwardFsyncRequest

这些request最终会被checkpointer进程读取,放入pendingOpsTable,而真正将脏页回写到磁盘的操作,是由checkpointer进程完成的。checkpointer每次也会调用smgrwrite,把所有的shared buffers脏页(即还没有被background writer清理过得脏页)写入操作系统的page cache,并存入pendingOpsTable。这样pendingOpsTable存放了所有write过的脏页,包括之前background writer已经处理的脏页。随后PostgreSQL的checkpointer进程会根据pedingOpsTable的记录,进行脏页回写操作(注意每次调用fysnc,都会sync数据表的一个文件,文件中所有脏页都会写入磁盘),调用顺序如下:

CheckPointGuts->CheckPointBuffers->->mdsync->pg_fsync->fsync

如果checkpointer做磁盘写入的频率过高,则每次可能只写入很少的数据。我们知道,磁盘对于顺序写入批量数据比随机写的效率要高的多,每次写入很少数据,就造成大量随机写;而如果我们放慢checkpoint的频率,多个随机页面就有可能组成一次顺序批量写入,效率大大提高。另外,checkpoint会进行fsync操作,大量的fsync可能造成系统IO阻塞,降低系统稳定性,因此checkpoint不能过于频繁。但checkpoint的间隔也不能无限制放大。因为如果出现系统宕机,在进行恢复时,需要从上一次checkpoint的时间点开始恢复,如果checkpoint间隔过长,会造成恢复时间缓慢,降低可用性。


相关参数配置

checkpoint_warning 
如果检查点触发的频率小于30s,则log日志中会提醒你增加max_wal_size。
参考设置:如果硬盘的性能很好可以设置小一些,如果硬盘性能差就设置大一些。这个参数不影响性能。只是用来检查实际checkpoint发生的频率。


checkpoint_timeout
自动检查点出发的时间,增加这个参数的值会增加崩溃恢复所需的时间。取值范围为30S≈1天。磁盘性能好的话可以适当减小。这个值减小会增加检查点频率,降低性能。
 
max_wal_size

在自动WAL检查点使得WAL增长到最大尺寸,到了这个尺寸,检查点就开始工作。
参考值:主机内存1/2,默认是1GB

min_wal_size
WAL磁盘使用率低于这个设置,旧的WAL文件总数被回收,确保预留足够的WAL空间处理WAL使用中的峰值。
参考值:主机内存1/8

checkpoint_completion_target
指定检查点完成的目标,作为检查点之间总时间的一部分。默认值是0.5,这个参数一般要结合checkpoint_timeout来使用。

图表

从上可知checkpoint_completion_target设置的越高的情况下,写入速度越低,对客户而言,体验越好,性能越高。反之,较低的值可能会引起I/O峰值,导致“卡死”的现象。

参考值:0.8≈0.9

checkpoint_flush_after
单位:BLCKSZ
当checkpointer process脏数据超过配置阈值时,触发调用OS sync_file_range,告诉os backend flush线程异步刷盘。从而削减os dirty page堆积
参考值:有效范围介于0(禁用强制写回)和2MB之间。Linux上的默认值为256KB,其他地方为0