• PostgreSQL故障恢复能力之检查点(Checkpoint)


    复习下前面的知识点:

    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)。

     演示

    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
    rmgr: Standby     len (rec/tot):     50/    50, tx:          0, lsn: 3/8B0000A0, prev 3/8B000028, desc: RUNNING_XACTS nextXid 58215 latestCompletedXid 58214 oldestRunningXid 58215
    rmgr: XLOG        len (rec/tot):    114/   114, tx:          0, lsn: 3/8B0000D8, prev 3/8B0000A0, desc: CHECKPOINT_SHUTDOWN redo 3/8B0000D8; 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 0; shutdown
    pg_waldump: fatal: error in WAL record at 3/8B0000D8: invalid record length at 3/8B000150: wanted 24, got 0

    5、再次启动数据库,可知数据库正常启动,无恢复操作

    但行好事,莫问前程
  • 相关阅读:
    【leetcode】153. 寻找旋转排序数组中的最小值
    vue下载网络图片
    前端开发项目细节
    如何在手机上预览本地h5页面
    react拖拽添加新组件
    js拖入并复制和拖动改变位置和改变大小
    dva model
    postMessage跨源通信
    react-router
    event.stopPropagation()和event.preventDefault(),return false的区别
  • 原文地址:https://www.cnblogs.com/mingfan/p/14829511.html
Copyright © 2020-2023  润新知