ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

PostgreSQL的WAL(3)--Checkpoint

2020-09-19 11:32:15  阅读:206  来源: 互联网

标签:11 checkpoint WAL -- Checkpoint wal 检查点 pg


我们已经熟悉了buffer cache的结构(共享内存的主要对象之一),并得出结论,要在所有RAM内容丢失后发生故障后恢复,必须保留预写日志(WAL)。

我们上次中断的地方尚未解决的问题是,我们不知道在恢复期间从哪里开始播放WAL记录。从头开始,这是不可行的:不可能从服务器启动时保留所有WAL记录-这可能既需要巨大的内存,又要很长的恢复时间。我们需要找到一个点,并且可以从该位置开始恢复(并相应地安全删除所有先前的WAL记录)。这就是我们要讲的检查点。

检查点

检查点必须具备哪些特点呢? 我们必须确保所有从检查点开始的WAL记录都将应用于刷新到磁盘的页面。如果不是这种情况,则在恢复期间,我们可能从磁盘上读取一个过旧的页面版本,对其应用WAL记录,这样做会不可逆转地损害数据。

我们如何获得检查点? 最简单的选择是不时暂停系统工作,并将缓冲区的所有脏页和其他高速缓存刷新到磁盘。(请注意,仅写入页面,而不从高速缓存中逐出页面)这些点将满足上述条件,但是时而连续"死亡"一段时间的系统,没有人会满意。

实际上这有点复杂:检查点从一个点变成一个间隔。首先,我们启动一个检查点。之后,我们悄悄地将脏缓冲区刷新到磁盘上,而不会中断工作或在任何可能的情况下导致峰值负载。

 

当所有在检查点开始时变脏的缓冲区都在磁盘上时,该检查点被视为已完成。现在(但不是更早),我们可以使用开始时间作为开始恢复的时间。而且我们不再需要到现在为止创建的WAL记录。

 

一个被称作检查点进程的后台进程执行检查点。

写入脏缓冲区的持续时间由checkpoint_completion_target参数定义。它显示了写入完成后两个相邻检查点之间的时间比例。默认值为0.5(如上图所示),即两次检查之间的写入时间占一半。通常,此值增加到1.0,以实现更高的均匀性。

让我们更详细地看看执行检查点时会发生什么。

首先,检查点将XACT缓冲区刷新到磁盘。由于它们很少(只有128个),因此它们会立即被写入。

然后,主要任务开始:从缓冲区高速缓存中刷新脏页。正如我们已经提到的,由于缓冲区高速缓存的大小可能很大,因此无法立即刷新所有页面。因此,buffer cache中所有当前脏的页面都用位于header中的特殊标志标记。

 

然后,检查点进程遍历所有缓冲区,并将标记的缓冲区刷新到磁盘。这里需要提醒你,页面不会从高速缓存中逐出,而只会写入磁盘。因此,你不必关注缓冲区的使用计数或是否被pin。

自然,在执行检查点时,buffer cache中的页面仍会继续被更新。但是不会标记新的脏缓冲区,并且检查点进程不会将它们写入磁盘。

 

在工作结束时,该过程将创建检查点末尾的WAL记录。该记录包含检查点开始时间的LSN。由于检查点启动时不会向WAL写入任何内容,因此任何日志记录都可以位于此LSN上。

此外,最后完成的检查点的指示在$PGDATA/global/pg_control文件中更新。在检查点完成之前,pg_control指向上一个检查点。

 

为了观看检查点的工作,让我们创建一个表。它的页面将进入buffer cache并称为脏页:

=> CREATE TABLE chkpt AS SELECT * FROM generate_series(1,10000) AS g(n);
=> CREATE EXTENSION pg_buffercache;
=> SELECT count(*) FROM pg_buffercache WHERE isdirty;
 count
-------
    78
(1 row)

让我们记住当前的WAL位置:

=> SELECT pg_current_wal_insert_lsn();
 pg_current_wal_insert_lsn
---------------------------
 0/3514A048
(1 row)

现在,让我们手动执行检查点,以确保高速缓存中不留任何脏页(正如我们已经提到的,可以出现新的脏页,但是在上述情况下,执行检查点时没有更改):

=> CHECKPOINT;
=> SELECT count(*) FROM pg_buffercache WHERE isdirty;
 count
-------
     0
(1 row)

让我们看一下检查点在WAL中的体现:

=> SELECT pg_current_wal_insert_lsn();
 pg_current_wal_insert_lsn
---------------------------
 0/3514A0E4
(1 row)

 

postgres$ /usr/lib/postgresql/11/bin/pg_waldump -p /var/lib/postgresql/11/main/pg_wal -s 0/3514A048 -e 0/3514A0E4
rmgr: Standby     len (rec/tot):     50/    50, tx:          0, lsn: 0/3514A048, prev 0/35149CEC, desc: RUNNING_XACTS nextXid 101105 latestCompletedXid 101104 oldestRunningXid 101105
rmgr: XLOG        len (rec/tot):    102/   102, tx:          0, lsn: 0/3514A07C, prev 0/3514A048, desc: CHECKPOINT_ONLINE redo 0/3514A048; tli 1; prev tli 1; fpw true; xid 0:101105; oid 74081; multi 1; offset 0; oldest xid 561 in DB 1; oldest multi 1 in DB 1; oldest/newest commit timestamp xid: 0/0; oldest running xid 101105; online

在这里看到两个记录。最后一个是检查点完成的记录(CHECKPOINT_ONLINE)。在单词“ redo”之后输出检查点开始的LSN,此位置对应于在检查点开始时间的最后一个WAL记录。

我们将在控制文件中找到相同的信息:

postgres$ /usr/lib/postgresql/11/bin/pg_controldata -D /var/lib/postgresql/11/main | egrep 'Latest.*location'
Latest checkpoint location:           0/3514A07C
Latest checkpoint's REDO location:    0/3514A048

恢复

现在,我们准备更准确地陈述上一篇文章中提及的恢复算法。

如果postgresql server出现故障,则在随后的启动中,启动过程会通过查看pg_control文件来查找与“shutdown”状态不同的状态。在这种情况下,将执行自动恢复。

首先,恢复过程将从pg_control文件读取检查点开始位置。(要完成此操作,如果backup_label文件可用,那么将从那里读取检查点记录-从备份中还原这是必需的,但这是另一个系列的主题。)

然后,恢复过程将从找到的位置开始读取WAL,并将WAL记录逐一应用于页面(如果有需要,正如我们上次讨论的那样)。

最后,all unlogged tables are emptied by their initialization forks.。

这是启动过程完成的工作,检查点进程立即执行检查点以保护磁盘上已还原的状态。

我们可以通过强制在immediate模式下关闭来模拟故障。

student$ sudo pg_ctlcluster 11 main stop -m immediate --skip-systemctl-redirect

(这里需要--skip-systemctl-redirect选项,因为我们使用安装在Ubuntu上的PostgreSQL。它由pg_ctlcluster命令控制,该命令实际上调用systemctl,而后者又调用pg_ctl。但是--skip-systemctl-redirect选项使我们无需systemctl即可执行操作并保留重要信息。)

让我们检查集群的状态:

postgres$ /usr/lib/postgresql/11/bin/pg_controldata -D /var/lib/postgresql/11/main | grep state
Database cluster state:               in production

启动时,PostgreSQL知道发生了故障,需要恢复。

student$ sudo pg_ctlcluster 11 main start

postgres$ tail -n 7 /var/log/postgresql/postgresql-11-main.log
2019-07-17 15:27:49.441 MSK [8865] LOG:  database system was interrupted; last known up at 2019-07-17 15:27:48 MSK
2019-07-17 15:27:49.801 MSK [8865] LOG:  database system was not properly shut down; automatic recovery in progress
2019-07-17 15:27:49.804 MSK [8865] LOG:  redo starts at 0/3514A048
2019-07-17 15:27:49.804 MSK [8865] LOG:  invalid record length at 0/3514A0E4: wanted 24, got 0
2019-07-17 15:27:49.804 MSK [8865] LOG:  redo done at 0/3514A07C
2019-07-17 15:27:49.824 MSK [8864] LOG:  database system is ready to accept connections
2019-07-17 15:27:50.409 MSK [8872] [unknown]@[unknown] LOG:  incomplete startup packet

日志中报告了需要恢复:数据库系统未正确关闭; 自动恢复正在进行中 然后在«redo starts at»位置开始播放WAL记录,并在可能获取下一个WAL记录的同时继续播放。这样就可以在«redo done at»位置完成恢复,并且DBMS开始与客户端一起工作(数据库系统已准备好接受连接)。

在服务器正常关闭时会发生什么? 要将脏页刷新到磁盘,PostgreSQL断开所有客户端的连接,然后执行最终检查点。

让我们记住当前的WAL位置:

=> SELECT pg_current_wal_insert_lsn();
 pg_current_wal_insert_lsn
---------------------------
 0/3514A14C
(1 row)

现在,我们以常规方式关闭:

student$ sudo pg_ctlcluster 11 main stop

检查集群状态:

postgres$ /usr/lib/postgresql/11/bin/pg_controldata -D /var/lib/postgresql/11/main | grep state
Database cluster state:               shut down

而且WAL具有最终检查点(CHECKPOINT_SHUTDOWN)的唯一记录:

postgres$ /usr/lib/postgresql/11/bin/pg_waldump -p /var/lib/postgresql/11/main/pg_wal -s 0/3514A14C
rmgr: XLOG        len (rec/tot):    102/   102, tx:          0, lsn: 0/3514A14C, prev 0/3514A0E4, desc: CHECKPOINT_SHUTDOWN redo 0/3514A14C; tli 1; prev tli 1; fpw true; xid 0:101105; oid 74081; multi 1; offset 0; oldest xid 561 in DB 1; oldest multi 1 in DB 1; oldest/newest commit timestamp xid: 0/0; oldest running xid 0; shutdown
pg_waldump: FATAL:  error in WAL record at 0/3514A14C: invalid record length at 0/3514A1B4: wanted 24, got 0

重新启动实例:

student$ sudo pg_ctlcluster 11 main start

后台写

如我们所知,检查点是将脏页从buffer cache刷新到磁盘的过程之一。但这不是唯一的。

如果后端进程需要从缓冲区刷新页面,但是该页面含有脏数据,则该进程将不得不自行将页面写入磁盘。这种情况并不好,因为它需要等待-在后台异步完成写入会更好。

因此,除了检查点进程外,还存在后台写进程(也称为bgwriter或仅称为writer)。该进程使用与驱逐技术相同的算法来搜索缓冲区。他们有两个区别。

1.后台写进程使用自己的指针,而不是指向«next victim»的指针。自己的指正可以在«next victim»的指针之前,但是永远不能在它之后。 2.遍历缓冲区时,使用计数不会减少。

 

被写出的buffer要满足以下条件:

·包含脏的数据

·没有被pin住(pin计数为0)

·使用计数为0

 

因此,后台写过程先于eviction,找到很可能很快被逐出的缓冲区。理想的情况是,后台写必须能够检测到他们选择的缓冲区可以被使用,而不会浪费写入时间。

调优

通常根据以下推理来设置检查点。

首先,我们需要确定在两个检查点之间可以负担多少数量的WAL记录(以及我们可以接受的恢复时间)。越多越好,但是出于明显的原因,该值是有限的。

然后,我们可以计算出在正常负载下生成此数量的wal所需的时间。我们已经讨论了如何执行此操作(我们需要记住WAL中的位置,并从另一个位置中减去一个)。

接着是检查点之间的通常间隔。设置checkpoint_timeout参数。默认值为5分钟,显然太短;通常会增加到半个小时。

但是有可能(甚至可能)有时负载会比平时更高,并且在参数指定的时间内会生成过多的WAL记录。在这种情况下,希望更频繁地执行检查点。为此,我们在max_wal_size参数中指定允许的WAL文件大小。如果实际量更多,则服务器将启动计划外的检查点。

服务器需要保留从最后一个完成的检查点开始的WAL文件以及当前检查点期间累积的文件。因此,可以将总数量估算为一个检查点周期中的数量乘以(1 + checkpoint_completion_target)。在版本11之前,我们应该乘以(2 + checkpoint_completion_target),因为PostgreSQL还保留了最后一个检查点中的文件。

因此,大多数检查点都按计划执行:每个checkpoint_timeout时间单位一次。但是在负载增加时,达到max_wal_size的数量时,检查点执行的频率会更高。

 

重要的是要理解可以超过max_wal_size参数的值:

·max_wal_size参数的值仅是理想值,而不是严格的限制。实际可能超过该值。

·server不能擦除尚未通过复制槽传递、或尚未归档的的wal文件,

 

可以通过min_wal_size参数指定最小值。

仅在调整检查点时才调整后台写才有意义。

后台写一次最多写bgwriter_lru_maxpages个页,在下一次写之前会根据bgwriter_delay的值sleep一段时间。

默认值为:bgwriter_delay = 200毫秒,bgwriter_lru_maxpages = 100。

如果根本找不到脏缓冲区(也就是说,系统中什么也没有发生),则它“进入休眠状态”。

监控

你需要根据监控结果来调优检查点进程和后台写。

如果wal数量太多,参数checkpoint_warning会输出警告提醒,默认值是30秒,我们需要将其调整到checkpoint_timeout的值。

参数log_checkpoints可以将检查点信息写入log。默认是不开启

=> ALTER SYSTEM SET log_checkpoints = on;
=> SELECT pg_reload_conf();

现在,让我们更改数据中的某些内容并执行检查点。

=> UPDATE chkpt SET n = n + 1;
=> CHECKPOINT;

在看看log文件的内容:

postgres$ tail -n 2 /var/log/postgresql/postgresql-11-main.log
2019-07-17 15:27:55.248 MSK [8962] LOG:  checkpoint starting: immediate force wait
2019-07-17 15:27:55.274 MSK [8962] LOG:  checkpoint complete: wrote 79 buffers (0.5%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.001 s, sync=0.013 s, total=0.025 s; sync files=2, longest=0.011 s, average=0.006 s; distance=1645 kB, estimate=1645 kB

我们可以在此处看到写入了多少缓冲区,在检查点之后更改了WAL文件集,执行检查点花费了多长时间以及相邻检查点之间的距离(以字节为单位)。

但是,最有用的信息可能是pg_stat_bgwriter视图中检查点和后台写进程的统计信息。

=> SELECT * FROM pg_stat_bgwriter \gx
-[ RECORD 1 ]---------+------------------------------
checkpoints_timed     | 0
checkpoints_req       | 1
checkpoint_write_time | 1
checkpoint_sync_time  | 13
buffers_checkpoint    | 79
buffers_clean         | 0
maxwritten_clean      | 0
buffers_backend       | 42
buffers_backend_fsync | 0
buffers_alloc         | 363
stats_reset           | 2019-07-17 15:27:49.826414+03

其中:

·checkpoints_timed--按计划(到达checkpoint_timeout时)。

·checkpoints_req--按需(包括在达到max_wal_size时执行的检查)。该值越大,表明检查点发生的越频繁。

 

以下是有关写入页数的重要信息:

·buffers_checkpoint--通过检查点。

·buffers_backend--通过后端进程。

·buffers_clean--通过后台写进程。

 

在一个经过良好调整的系统中,buffers_backend的值必须小于buffers_checkpoint和buffers_clean的总和。

参数maxwrite_clean的值也将有助于调整后台写。它显示由于超出bgwriter_lru_maxpages的值而使进程停止了多少次。

可以在重置收集的统计信息:

=> SELECT pg_stat_reset_shared('bgwriter');

  

 

 

原文地址:https://habr.com/en/company/postgrespro/blog/494464/

 

 

标签:11,checkpoint,WAL,--,Checkpoint,wal,检查点,pg
来源: https://www.cnblogs.com/abclife/p/13679280.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有