复习下前面的知识点:
1、缓冲区高速缓存(buffer cache)位于服务器的共享内存中,并且所有进程均可访问。在读取或更新数据时,进程将页面读入缓存。当页面位于缓存中时,我们在ram中使用它并保存数据到磁盘。
2、当遇到掉电等故障场景,所有 ram 内容丢失时,要在故障后恢复数据,pg必须维护一份预写式日志(wal),在故障恢复过程中通过重放wal日志进行数据恢复。
checkpoint
本文需要讨论的是,我们不知道在恢复期间从哪里开始重放 wal 记录。
方案一:
从头开始恢复,带来是问题是我们不一定保留了所有历史的wal日志记录(保留所有历史wal会占用大量磁盘空间),即使我们通过归档保留了所有的wal记录,恢复的时长也会难以接受。
方案二:
我们需要一个逐渐向前推进的时间点,我们可以从该时间点开始恢复(并可以安全地删除时间点以前的wal 记录),为此,pg引入了检查点(checkpoint)。
当执行checkpoint后,会确保检查点开始时所有脏的缓冲区都刷到磁盘上,此时checkpoint执行完成。后续我们可以使用checkpoint记录的开始时间作为开始恢复的点。
checkpoint会执行多久
checkpoint执行后会带来大量的页面写入操作,为了避免大量的页面写入对i/o造成冲击,在检查点期间写入脏缓冲区的过程会分散为一段时间。该周期由checkpoint_completion_target控制,它是检查点间隔的一部分,默认为0.5,也就是说每个checkpoint需要在checkpoints间隔时间的50%内完成。
通常checkpoint_completion_target值会增加到 0.9 以获得更好的均匀性,postgresql 14版本中会将默认值修改为0.9。
checkpoint执行过程
1、首先会对缓冲区中的脏页进行标记
2、然后检查指针遍历所有缓存并将标记的脏页刷新到磁盘(页面不会从缓存中逐出,而只会写入磁盘)。
3、新的脏缓冲区不会被标记,并且检查指针不会写入它们。
4、创建检查点结束的 wal 记录,该记录包含检查点开始时间的 lsn。
5、更新控制文件信息($pgdata/global/pg_control)。
checkpoint 演示
1、创建一个表;它的页面将进入缓冲区缓存并变脏:
create extension pg_buffercache;
create table chkpt as select * from generate_series(1,10000) as g(n);
select count(*) from pg_buffercache where isdirty;
postgres=# select count(*) from pg_buffercache where isdirty;
count
-------
62
(1 row)
2、记住当前的 wal 位置
select pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn
---------------------------
3/8a0b3810
(1 row)
3、手动执行检查点操作
checkpoint;
select count(*) from pg_buffercache where isdirty;
count
-------
0
(1 row)
4、看看检查点是如何反映在 wal 中的
select pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn
---------------------------
3/8a0b38f8
(1 row)
可以看到lsn有更新,接下来用pg_waldump查看wal日志记录了哪些内容
查看当前lsn对应的wal日志文件
select file_name, upper(to_hex(file_offset)) file_offset from pg_walfile_name_offset('3/8a0b38f8');
file_name | file_offset
-------------------------- -------------
00000001000000030000008a | b38f8
(1 row)
/usr/local/pgsql/bin/pg_waldump -p usr/local/pgsql/data/pg_wal/ -s 3/8a0b3810 -e 3/8a0b38f8 00000001000000030000008a
----
rmgr: standby len (rec/tot): 50/ 50, tx: 0, lsn: 3/8a0b3810, prev 3/8a0b37d8, desc: running_xacts nextxid 58215 latestcompletedxid 58214 oldestrunningxid 58215
rmgr: xlog len (rec/tot): 114/ 114, tx: 0, lsn: 3/8a0b3848, prev 3/8a0b3810, desc: checkpoint_online redo 3/8a0b3810; tli 1; prev tli 1; fpw true; xid 0:58215; oid 34053;
multi 1; offset 0; oldest xid 479 in db 1; oldest multi 1 in db 12723; oldest/newest commit timestamp xid: 0/0; oldest running xid 58215; online
可以看到日志记录了checkpoint_online信息,checkpoint start的lsn在单词“redo”之后输出,这个位置对应checkpoint start时间最后一个wal记录。
5、查看控制文件信息,可知控制文件记录了最近的checkpoint点对应的lsn信息
pg_controldata -d usr/local/pgsql/data/ |egrep 'latest.*location'
latest checkpoint location: 3/8a0b3848
latest checkpoint's redo location: 3/8a0b3810
故障后恢复
如果服务器出现故障,则在后续启动时,会通过查看pg_control控制
文件,查找与“关闭”不同的状态来检测此情况。在这种情况下进行自动恢复。
1、直接kill掉postgres主进程
2、查看控制文件database cluster state状态,没有发生变化
pg_controldata -d usr/local/pgsql/data/ |grep state
--------
database cluster state: in production
3、启动数据库,查看日志
pg_ctl -d /usr/local/pgsql/data/ start
可知故障后,数据库从latest checkpoint's redo location开始恢复数据
正常关闭后恢复
1、记录当前的lsn
select pg_current_wal_insert_lsn();
pg_current_wal_insert_lsn
---------------------------
3/8b0000a0
(1 row)
2、正常关闭数据库
pg_ctl -d /usr/local/pgsql/data/ stop
3、查看控制文件database cluster state状态,可知正确记录了关闭状态
pg_controldata -d /usr/local/pgsql/data/ |grep state
database cluster state: shut down
4、查看wal日志, 拥有最终检查点的唯一记录(checkpoint_shutdown)
/usr/local/pgsql/bin/pg_waldump -p /usr/local/pgsql/data/pg_wal/ -s 3/8b0000a0 00000001000000030000008b
5、再次启动数据库,可知数据库正常启动,无恢复操作