一、 检查点进程
在pg中,当下列情形之一发生时,checkpointer进程会启动执行检查点:
- 自上次检查点执行时间已超过checkpoint_timeout设置(默认300秒)。
- 在9.5之前,自上一次检查点以来使用的WAL文件数量,超过了checkpoint_segments设置(默认为3)。
- 9.5开始为pg_xlog中WAL文件总大小超过max_wal_size设置(默认1GB,64个文件)。
- 以smart或fast模式关闭pg。
- 超级用户手动发出CHECKPOINT命令
在9.1之前,bgwriter进程同时负责checkpoint和脏页写。
以下将简要描述检查点进程和保存当前检查点元数据的pg_control文件。
1. 检查点进程概述
检查点进程负责两个工作:
- 为崩溃恢复做准备(本节主要描述)
- 将共享缓冲池中的脏页刷盘
(1)检查点进程启动时,会将重做点存储在内存中。重做点是最新的检查点开始时xlog记录写入的位置,也是崩溃恢复的起点。
(2)将此检查点对应的XLOG记录(即检查点记录)写入WAL缓冲区。该记录的数据部分由CheckPoint结构体定义,结构体包含一些变量,如步骤(1)中重做点的位置。此外,写入检查点记录的位置按照字面意思也称作检查点。
(3,4)刷新共享缓冲池中的所有数据(例如CLOG的内容等)到磁盘。
(5)更新pg_control文件,该文件包含一些基本信息,例如检查点记录所写的位置,稍后将详细描述。
checkpoint结构体定义:
2. pg_control文件
由于pg_control文件包含检查点的基本信息,因此它对于崩溃恢复是必不可少的。如果它被破坏或不可读,系统将无法得知恢复起点,无法进行恢复。
尽管pg_control文件存储了40多个项目,但主要有三项与崩溃恢复相关:
- State:最新检查点开始时数据库的状态。共有七个:start up表示数据库正在启动;shutdown表示正常关闭;in production表示数据库正在运行,等等。
- Latest checkpoint location:最新检查点的LSN位置。
- Prior checkpoint location:前一个检查点的LSN位置,pg 11开始已弃用(节约用于WAL文件的磁盘空间)。
pg_control文件存储在global子目录中,可以使用pg_controldata命令查看其内容。
-bash-4.2$ pg_controldata $PGDATA
pg_control version number: 1002
Catalog version number: 201707211
Database system identifier: 6748611403308013153
Database cluster state: in archive recovery <---------
pg_control last modified: Mon 21 Oct 2019 04:10:29 PM CST
Latest checkpoint location: 0/1B0003D8 <---------
Prior checkpoint location: 0/1B000028 <---------
Latest checkpoint's REDO location: 0/1B0003A0
Latest checkpoint's REDO WAL file: 00000007000000000000001B
Latest checkpoint's TimeLineID: 7
Latest checkpoint's PrevTimeLineID: 7
Latest checkpoint's full_page_writes: on
Latest checkpoint's NextXID: 0:587
Latest checkpoint's NextOID: 32800
Latest checkpoint's NextMultiXactId: 1
Latest checkpoint's NextMultiOffset: 0
Latest checkpoint's oldestXID: 548
Latest checkpoint's oldestXID's DB: 1
Latest checkpoint's oldestActiveXID: 587
Latest checkpoint's oldestMultiXid: 1
Latest checkpoint's oldestMulti's DB: 1
Latest checkpoint's oldestCommitTsXid:0
Latest checkpoint's newestCommitTsXid:0
Time of latest checkpoint: Mon 21 Oct 2019 04:02:36 PM CST
Fake LSN counter for unlogged rels: 0/1
Minimum recovery ending location: 0/1B000480
Min recovery ending loc's timeline: 7
Backup start location: 0/0
Backup end location: 0/0
End-of-backup record required: no
wal_level setting: replica
wal_log_hints setting: off
max_connections setting: 1000
max_worker_processes setting: 8
max_prepared_xacts setting: 0
max_locks_per_xact setting: 64
track_commit_timestamp setting: off
Maximum data alignment: 8
Database block size: 8192
Blocks per segment of large relation: 131072
WAL block size: 8192
Bytes per WAL segment: 16777216
Maximum length of identifiers: 64
Maximum columns in an index: 32
Maximum size of a TOAST chunk: 1996
Size of a large-object chunk: 2048
Date/time type storage: 64-bit integers
Float4 argument passing: by value
Float8 argument passing: by value
Data page checksum version: 0
Mock authentication nonce: c93bf5a4c60af9b458db7cc843906e084d4228c160ff96f11af92e2ceeacf5a7
二、 崩溃恢复
pg实现了基于重做日志的崩溃恢复。如果数据库服务器崩溃,pg通过从重做点按序重放WAL文件中的XLOG记录来恢复数据库集群。
在本节之前我们已经多次讨论了数据库恢复,因此这里仅介绍两个与恢复有关而尚未解释的内容。
第一是pg如何启动恢复过程。当pg启动时,它首先读取pg_control文件,恢复过程如下:
- (1)pg在启动时读取pg_control文件内容。如果state为’in production’,PostgreSQL将进入恢复模式,因为这意味着数据库没有正常停止;如果为’shutdown’,将进入正常启动模式。
- (2)pg从相应的WAL段文件中读取最新的检查点记录(位于pg_control文件中),并从记录中获取重做点。如果最新的检查点记录无效(invalid),pg将读取前一个检查点的记录。如果两个记录都不可读,将放弃恢复。注意,从11版本开始不会再存储前一个检查点的记录信息。
- (3)使用合适的资源管理器从重做点开始按顺序读取和重放XLOG记录,直到最新WAL文件的最后位置。当遇到备份块时,无论其LSN如何,都会将覆盖相应表的页面。否则仅当此xlog记录LSN>相应页面的pd_lsn时,才会重放该XLOG记录。
第二点是关于LSN的比较。为什么应该比较非备份块xlog记录的LSN与相应页面的pd_lsn。这里使用一个特定示例来解释强制两个LSN的比较需求。注意这里省略了WAL缓冲区以简化描述。
(1)向TABLE_A中插入一个元组,并在LSN_1处写入一条XLOG记录。
(2)bgwriter 将TABLE_A的页面写入磁盘。此时,此页面的pd_lsn为LSN_1。
(3)向TABLE_A中再插入一个元组,并在LSN_2处写入一条XLOG记录,修改后的页面还没有写入磁盘。
与前面的示例不同,本场景中,TABLE_A的页面已写入磁盘。
使用immediate模式关闭,然后启动,恢复过程如下:
(1)pg加载第一个XLOG记录和TABLE_A的页面,但不重放,因为该xlog记录的LSN不大于TABLE_A的LSN(都是LSN_1)。实际也可以看到完全没有重放的必要性。
(2)接下来,pg会重放第二个XLOG记录,因为该记录的LSN(LSN_2)大于当前TABLE_A的LSN(LSN_1)。
可以看出,如果非备份块的重放顺序不正确或者被多次重放,数据库集群将不一致。简而言之,非备份块的重放操作不是幂等的。因此,为了保留正确的重放顺序,当且仅当其LSN大于相应页面的pd_lsn时,才重放非备份块记录。
另一方面,由于备份块的重做操作是幂等的,因此备份块可以重放任意次,而不管其LSN如何。
注:在数学与计算机学中,幂等操作是指其执行任意多次所产生的影响均与执行一次相同,即f(f(x))=f(x)。
参考
The Internals of PostgreSQL : Chapter 9 Write Ahead Logging — WAL