第二章 checkpoint机制 - 介绍

2024-05-11 08:20

本文主要是介绍第二章 checkpoint机制 - 介绍,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Checkpoint介绍

1、介绍

PostgreSQL基于DRAM+HDD/SSD两层存储架构,所有读写都在DRAM中进行。由于数据写大部分都是随机的,为提高数据库性能,通过写前日志WAL将所有修改都记录到日志中,事务提交时,将日志持久化到磁盘即可,而脏数据页由后台进程异步刷写磁盘,从而将随机写转换成顺序写。

崩溃重启时通过WAL日志将修改回放出来,从而保证数据持久性和一致性,但是如果所有事务的WAL日志都需要回放的话,一旦日志量非常大时,恢复时间就会很长。为减小恢复时间,PostgreSQL通过checkpoint将脏数据刷写到磁盘,崩溃恢复时,只需将checkpoint之后的日志回放即可。

PostgreSQL由checkpoint进程完成刷写脏页(还有一个后台进程bgwriter也会刷写脏页,本文不做过多介绍),并生成一个CHECKPOINT的WAL日志,同时将checkpoint位置记录到pg_control文件中。重启恢复时,从pg_control文件中读取checkpoint位置,作为读取WAL日志和回放的起始位置。启动后可以看到checkpoint进程:

80259 ? Ss 0:00 /home/yzs/bin/postmaster -D /home/yzs/data/
80260 ? Ss 0:00 \_ postgres: logger
80262 ? Ss 0:00 \_ postgres: checkpointer
80263 ? Ss 0:00 \_ postgres: background writer
80264 ? Ss 0:00 \_ postgres: walwriter
80265 ? Ss 0:00 \_ postgres: autovacuum launcher
80266 ? Ss 0:00 \_ postgres: stats collector
80267 ? Ss 0:00 \_ postgres: autoprewarm master
80268 ? Ss 0:00 \_ postgres: logical replication launcher

当将checkpoint子进程kill -9杀掉后,其他进程也会重新启动,这也说明checkpoint子进程对数据一致性有很重要保护作用。

pg_control文件记录的checkpoint信息:

2、checkpoint相关参数

1)log_checkpoints

该参数默认关闭,开启后将每次checkpoint详细信息记录到错误日志中,可以通过此信息了解系统负载。一个相关信息如下:

“checkpoint starting: immediate force wait”//这条信息由函数LogCheckpointStart输出
“checkpoint complete: wrote 1 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.000 s, sync=0.000 s, total=0.001 s; sync files=1, longest=0.000 s, average=0.000 s; distance=0 kB, estimate=11948 kB”//这条信息由函数LogCheckpointEnd输出

2)checkpoint_warning

该参数默认30s,如果填充的WAL段文件导致检查点之间的间隔低于这个参数表示的时间,就向服务器日志写一个消息。0表示关闭警告。如果checkpoint_timeout小于checkpoint_warning值,则不会有警告产生。如果磁盘性能好,可以适当将这个值设置小点,但这个参数不会影响性能,仅用于检查检查点的频率。

3)checkpoint_timeout

自动检查点之间的最长时间。如果没有指定单位,则以秒为单位。默认值是5分钟。增加这个值,会增加崩溃恢复的时间。

checkpoint_timeout用到的位置:

  • checkpoint进程距上次检查点时间超过这个值,就强制触发检查点。在3节 checkpoint发生时机详细介绍。
  • Checkpoint进程执行完一次检查点后,如果执行的时间不够checkpoint_timeout 时间,则等待直到达到这个时间,流程简化后:
CheckpointerMainfor(;;){now = (pg_time_t) time(NULL);CreateCheckPoint(flags);//执行检查点last_checkpoint_time = now;//检查点开始时间now = (pg_time_t) time(NULL);elapsed_secs = now - last_checkpoint_time;//检查点执行的时间if (elapsed_secs >= CheckPointTimeout)continue;//检查点超时,不进行休眠cur_timeout = CheckPointTimeout - elapsed_secs;(void) WaitLatch(MyLatch,WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,cur_timeout * 1000L /* convert to ms */ ,WAIT_EVENT_CHECKPOINTER_MAIN);//检查点休眠}
  • 每次刷写完一个脏页后进行检查点调度判断,若刷写IO太过频繁则休息100ms
BufferSync->CheckpointWriteDelay->|-- IsCheckpointOnSchedulegettimeofday(&now, NULL);elapsed_time = ((double) ((pg_time_t) now.tv_sec - ckpt_start_time) +now.tv_usec / 1000000.0) / CheckPointTimeout;//脏页刷写率大于checkpoint消耗时间率的话,需要sleep 100msif (progress < elapsed_time){ckpt_cached_elapsed = elapsed_time;return false;}|-- pg_usleep(100000L);

 注意:IsCheckpointOnSchedule中除了判断时间的进度外,会先对日志产生的进度进行判断,从而对刷新脏页进行限速

if (RecoveryInProgress())recptr = GetXLogReplayRecPtr(NULL);//恢复场景:回放的WAL日志lsn
elserecptr = GetInsertRecPtr();//产生的WAL的lsn
elapsed_xlogs = (((double) (recptr - ckpt_start_recptr)) /wal_segment_size) / CheckPointSegments;//检查点以来产生WAL的进度
if (progress < elapsed_xlogs){ckpt_cached_elapsed = elapsed_xlogs;return false;
}

4)min_wal_size

用于回收WAL文件时设置未来日志文件段号值。段号值最小即为min_wal_size计算所得。所以只要WAL磁盘用量保持在这个设置之下,在检查点时旧的文件总是被回收以便未来使用,而不是直接删除。这个可以用来确保足够的WAL空间被保留下来应付WAL使用高峰,例如大量写任务下。如果指定时没有单位,则以MB为单位,默认80MB。

执行完checkpoint后,回收WAL文件时,用于判断回收后日志文件段号的范围:

XLOGfileslopminSegNo = RedoRecPtr / wal_segment_size +ConvertToXSegs(min_wal_size_mb, wal_segment_size) - 1;maxSegNo = RedoRecPtr / wal_segment_size +ConvertToXSegs(max_wal_size_mb, wal_segment_size) - 1;distance = (1.0 + CheckPointCompletionTarget) * CheckPointDistanceEstimate;/* add 10% for good measure. */distance *= 1.10;recycleSegNo = (XLogSegNo) ceil(((double) RedoRecPtr + distance) /wal_segment_size);if (recycleSegNo < minSegNo)recycleSegNo = minSegNo;if (recycleSegNo > maxSegNo)recycleSegNo = maxSegNo;return recycleSegNo;

 说明:回收WAL日志文件后,增加的段号要在min_wal_size和max_wal_size范围内

5)max_wal_size

自动WAL检查点之间允许WAL增长到的最大尺寸。这是一个软限制,特殊情况下WAL尺寸会超过max_wal_size。如果没有指定单位,则以MB为单位,默认1GB。增加这个参数会增加崩溃恢复时间。

在回收WAL文件时,预留的日志文件号最大是这个值计算出的段文件号。也就是说超过这量的日志需要删除。

另外就是和checkpoint_complete_target联合使用。

用到该参数的地方除了上文介绍的回收WAL日志文件处外,还用于计算CheckPointSegments值(checkpoint触发值),该值同样用于判断是否进行检查点调度。该值计算方法:

CalculateCheckpointSegmentstarget = (double) ConvertToXSegs(max_wal_size_mb, wal_segment_size) /(1.0 + CheckPointCompletionTarget);CheckPointSegments = (int) target;if (CheckPointSegments < 1)CheckPointSegments = 1;

说明:如果检查点不能立即完成,则老WAL日志就不能立即删除。max_wal_size规定了WAL日志的最大值。达到WAL日志大小的顶峰是checkpoint即将执行完成的时刻,因为此时包含这次触发的WAL日志加上新增的WAL日志。假设触发值是target,则checkpoint_timeout时间内,WAL日志新增大小最多为target。假设WAL日志增长速度相同,则此时增长的日志大小为target*checkpoint_completion_target,为保证峰值,wal日志大小是max_wal_size,由此可以计算出触发值:

target+target*checkpoint_completion_target=max_wal_size
target=max_wal_size/(1+checkpoint_completion_target)

6)checkpoint_completion_target

该参数表示checkpoint的完成时间占两次checkpoint时间间隔的比例,默认值是0.5,也就是说每个checkpoint需要在checkpoint时间间隔checkpoint_timeout的50%内完成。Checkpoint执行会消耗大量系统资源,尤其时IO资源,需要对IO进行限速以减少系统影响,使得磁盘IO变得平缓。

这个值除了在计算WAL日志大小触发checkpoint阈值(上小节介绍)、计算回收日志后最大段号(min_wal_size部分介绍)外,还在判断是否进行checkpoint调度时用到:

IsCheckpointOnScheduleprogress *= CheckPointCompletionTarget;//将刷脏页率缩放后,用于后续判断....

7)checkpoint_flush_after

自动上次checkpoint以来刷写了这么多脏页后,发起sync操作。这样做限制操作系统缓冲中脏页数量,降低checkpoint末尾发出sync或者OS在后台大批量写回数据时被卡住的可能性。如果没有指定单位,默认是页数,指定MB则转换成页数。

由函数ScheduleBufferTagForWriteback进行sync调度。

SyncOneBufferFlushBuffer(bufHdr, NULL);//刷写一个脏页tag = bufHdr->tag;ScheduleBufferTagForWriteback(wb_context, &tag);//调度是否sync|--	if (*context->max_pending > 0){//将脏页sync请求放入请求队列pending = &context->pending_writebacks[context->nr_pending++];pending->tag = *tag;}if (context->nr_pending >= *context->max_pending)IssuePendingWritebacks(context);//若请求数>=checkpoint_flush_after则sync//context->max_pending来自checkpoint_flush_afterBufferSync->WritebackContextInit(&wb_context, &checkpoint_flush_after);context->max_pending = max_pending;context->nr_pending = 0;

3、checkpoint发生时机

Checkpoint触发时机分为以下多种:

1)时间触发:后台checkpoint进程,距上次执行时间超过checkpoint_timeout值就会触发。由时间超时触发的时机:

CheckpointerMain:for (;;){now = (pg_time_t) time(NULL);elapsed_secs = now - last_checkpoint_time;//距上次检查点时间if (elapsed_secs >= CheckPointTimeout){//超过checkpoint_timeoutdo_checkpoint = true;flags |= CHECKPOINT_CAUSE_TIME;//发起CHECKPOINT_CAUSE_TIME的检查点}if (do_checkpoint){CreateCheckPoint(flags);last_checkpoint_time = now;//checkpoint开始时间}...}

2)数据库正常关闭时,触发一次:

CheckpointerMainpqsignal(SIGUSR2, ReqShutdownHandler);/* 注册信号处理函数 */for (;;){if (shutdown_requested){//checkpoint进程接收到shutdown请求ExitOnAnyError = true;ShutdownXLOG(0, 0);|--	CreateCheckPoint(CHECKPOINT_IS_SHUTDOWN | CHECKPOINT_IMMEDIATE);proc_exit(0);}...}
//接收到信号SIGUSR2后,发起shutdown checkpoint
static void ReqShutdownHandler(SIGNAL_ARGS)
{int			save_errno = errno;shutdown_requested = true;SetLatch(MyLatch);errno = save_errno;
}

3)数据库崩溃恢复,修复完成后触发一次:

StartupXLOG//为恢复进程CreateCheckPoint(CHECKPOINT_END_OF_RECOVERY | CHECKPOINT_IMMEDIATE);

4)手动执行CHECKPOINT命令

5)基础备份:当进行数据库基础备份时,会执行pg_start_backup->do_pg_start_backup触发一次:

do_pg_start_backupRequestCheckpoint(CHECKPOINT_FORCE | CHECKPOINT_WAIT |(fast ? CHECKPOINT_IMMEDIATE : 0));

6)每次将日志XLogWrite写入磁盘并且一个WAL日志文件写满后,自从上次checkpoint以来产生的超过max_wal_size放大checkpoint_completion_target后触发一次:

XLogWriteif (finishing_seg){//写满一个WAL段文件if (IsUnderPostmaster && XLogCheckpointNeeded(openLogSegNo)){(void) GetRedoRecPtr();if (XLogCheckpointNeeded(openLogSegNo))RequestCheckpoint(CHECKPOINT_CAUSE_XLOG);}}
//判断WAL自从上次checkpoint以来产生量是否超过阈值
static bool XLogCheckpointNeeded(XLogSegNo new_segno)
{XLogSegNo	old_segno;XLByteToSeg(RedoRecPtr, old_segno, wal_segment_size);if (new_segno >= old_segno + (uint64) (CheckPointSegments - 1))return true;return false;
}
//其中CheckPointSegments来自函数CalculateCheckpointSegments,由max_wal_size放大
//checkpoint_completion_target倍后计算得来,在第2节的max_wal_size中介绍。

4、checkpoint监控

除了log_checkpoints监听日志的方式外,还可以通过pg_stat_bgwriter视图查看checkpoint。下面介绍与checkpoint相关的列:

checkpoints_timed

因为时间触发的次数

checkpoints_req

其他原因触发的次数

checkpoint_write_time

缓存刷写到文件系统cache花费的时间

checkpoint_sync_time

缓存对应的文件系统cache刷写到磁盘花费的时间

buffers_checkpoint

buffer刷新到磁盘的数目

buffers_backend

checkpoint接收到sync请求次数

buffers_backend_fsync

后台因为发送sync请求失败,需要自身执行fsync操作的次数

stats_reset

更新时间

这篇关于第二章 checkpoint机制 - 介绍的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/979001

相关文章

5 种使用Python自动化处理PDF的实用方法介绍

《5种使用Python自动化处理PDF的实用方法介绍》自动化处理PDF文件已成为减少重复工作、提升工作效率的重要手段,本文将介绍五种实用方法,从内置工具到专业库,帮助你在Python中实现PDF任务... 目录使用内置库(os、subprocess)调用外部工具使用 PyPDF2 进行基本 PDF 操作使用

JAVA实现Token自动续期机制的示例代码

《JAVA实现Token自动续期机制的示例代码》本文主要介绍了JAVA实现Token自动续期机制的示例代码,通过动态调整会话生命周期平衡安全性与用户体验,解决固定有效期Token带来的风险与不便,感兴... 目录1. 固定有效期Token的内在局限性2. 自动续期机制:兼顾安全与体验的解决方案3. 总结PS

详解Spring中REQUIRED事务的回滚机制详解

《详解Spring中REQUIRED事务的回滚机制详解》在Spring的事务管理中,REQUIRED是最常用也是默认的事务传播属性,本文就来详细的介绍一下Spring中REQUIRED事务的回滚机制,... 目录1. REQUIRED 的定义2. REQUIRED 下的回滚机制2.1 异常触发回滚2.2 回

Java中HashMap的用法详细介绍

《Java中HashMap的用法详细介绍》JavaHashMap是一种高效的数据结构,用于存储键值对,它是基于哈希表实现的,提供快速的插入、删除和查找操作,:本文主要介绍Java中HashMap... 目录一.HashMap1.基本概念2.底层数据结构:3.HashCode和equals方法为什么重写Has

基于Redis自动过期的流处理暂停机制

《基于Redis自动过期的流处理暂停机制》基于Redis自动过期的流处理暂停机制是一种高效、可靠且易于实现的解决方案,防止延时过大的数据影响实时处理自动恢复处理,以避免积压的数据影响实时性,下面就来详... 目录核心思路代码实现1. 初始化Redis连接和键前缀2. 接收数据时检查暂停状态3. 检测到延时过

Redis中哨兵机制和集群的区别及说明

《Redis中哨兵机制和集群的区别及说明》Redis哨兵通过主从复制实现高可用,适用于中小规模数据;集群采用分布式分片,支持动态扩展,适合大规模数据,哨兵管理简单但扩展性弱,集群性能更强但架构复杂,根... 目录一、架构设计与节点角色1. 哨兵机制(Sentinel)2. 集群(Cluster)二、数据分片

Springboot项目构建时各种依赖详细介绍与依赖关系说明详解

《Springboot项目构建时各种依赖详细介绍与依赖关系说明详解》SpringBoot通过spring-boot-dependencies统一依赖版本管理,spring-boot-starter-w... 目录一、spring-boot-dependencies1.简介2. 内容概览3.核心内容结构4.

深入理解go中interface机制

《深入理解go中interface机制》本文主要介绍了深入理解go中interface机制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学... 目录前言interface使用类型判断总结前言go的interface是一组method的集合,不

C# async await 异步编程实现机制详解

《C#asyncawait异步编程实现机制详解》async/await是C#5.0引入的语法糖,它基于**状态机(StateMachine)**模式实现,将异步方法转换为编译器生成的状态机类,本... 目录一、async/await 异步编程实现机制1.1 核心概念1.2 编译器转换过程1.3 关键组件解析

setsid 命令工作原理和使用案例介绍

《setsid命令工作原理和使用案例介绍》setsid命令在Linux中创建独立会话,使进程脱离终端运行,适用于守护进程和后台任务,通过重定向输出和确保权限,可有效管理长时间运行的进程,本文给大家介... 目录setsid 命令介绍和使用案例基本介绍基本语法主要特点命令参数使用案例1. 在后台运行命令2.