postgresql 手动清理wal日志的101个坑
新年的第一天,总结下去年遇到的关于WAL日志清理的101个坑,以及如何相对安全地进行清理。前面是关于WAL日志堆积的原因分析,清理相关可以直接看第三部分。
首先说明,手动清理wal日志是一个高风险的操作,尤其对于带主从的生产大库,能不用尽量不要用。
一、 常见的WAL 堆积原因
其实之前有总结过(第8题) 《PostgreSQL面试题集锦》学习与回答-CSDN博客
去年遇到的两次都是因为WAL日志生成量过大,而pg 10中归档进程又仅支持单进程串行归档,下面单独列一下这个问题。
二、 PG 10中WAL日志归档的瓶颈
1. 归档流程
每当WAL日志切换时,就可以通知对该日志进行归档:
- 产生日志切换的进程在pg_wal/archive_status下生成与待归档日志同名的.ready文件
- 发送信号通知archiver process(我们是pgbackrest的archive push),其只关心是否有.ready文件存在,不关心其内容
- archiver process(pgbackrest的archive push)按照archive_command进行日志归档
- 执行完成后,archiver process循环将.ready文件重命名为.done文件
2. 归档的瓶颈
由于我们使用的是pgbackrest,在上面步骤中,前三步都可以并行完成,唯独第四步,需要靠archiver process单独工作(pg 15版本引入了新特性,可以64个文件为一组进行操作)。
/** pgarch_ArchiverCopyLoop** Archives all outstanding xlogs then returns*/
static void
pgarch_ArchiverCopyLoop(void)
{char xlog[MAX_XFN_CHARS + 1];/** 循环处理.ready文件*/while (pgarch_readyXlog(xlog)){int failures = 0;int failures_orphan = 0;for (;;){struct stat stat_buf;char pathname[MAXPGPATH];…/* 进行日志归档 */if (pgarch_archiveXlog(xlog)){/* successful,归档成功,将.ready改为.done文件 */pgarch_archiveDone(xlog);/** Tell the collector about the WAL file that we successfully archived*/pgstat_send_archiver(xlog, false);/* 开始处理下一个日志 */break; /* out of inner retry loop */}/* 归档失败 */
...}}
}
在WAL日志量不大,archive_status中文件不多时,循环查找文件并重命名是很快的,经观察:
- 在archive_status中约5000个文件时,archiver process每秒可以处理8~10个.ready文件
- 而在问题期间,archive_status中日志多达百万个,每次循环异常耗时,每秒仅能处理3~4个.ready文件(对应每小时约168~225G WAL日志)
- 在业务高峰期,每小时产生WAL日志基本都在200G以上,有时高达400G,远远高于其处理速度,导致archive_status目录中堆积文件更多,处理速度更慢
- 由于archiver process进程处理速度远远落后pgbackrest执行的archive_command命令,pgbackrest也认为自己无需再工作,因此未再启动
在pg中,只有已经归档的WAL日志(生成.done文件的)才能被回收或删除,由于归档速度过慢,导致可以被清理的WAL日志很少,而新增的WAL日志量又很大,最终导致WAL日志堆积,/data目录剩余空间持续下降。
...if (XLogArchiveCheckDone(xlde->d_name)){/* Update the last removed location in shared memory first,首先在共享内存中更新已被删除的位置 */UpdateLastRemovedPtr(xlde->d_name);/* 调用RemoveXlogFile函数进行删除或重命名,函数里使用unlink删除日志 */RemoveXlogFile(xlde->d_name, recycleSegNo, &endlogSegNo);}
...
三、 WAL日志清理的101个坑
1. WAL日志的重命名
非常危险的一点,概括而言,pg会将最旧的一些wal日志重命名为最新的名字,但利用ll -rth排序查看时,其时间并不会变化。而pg_archivecleanup工具只根据文件名进行判断,很可能导致需要的文件被删除。具体分析参考postgresql源码学习(58)—— 删除or重命名WAL日志?这是一个问题、-CSDN博客
如下图,实际上FE和FF结尾的文件是重命名后的,但显示的时间并没有变化,按时间排序还是在最上面。

① 主库需要的日志被删除
这是最危险的情况,可能导致:
- 主库crash:在某些情况下,可能会由于数据不一致或者持续性错误导致主库宕机。这点目前没有测试出来,但风险肯定是存在的。
- 主库crash后无法启动:删除wal日志后kill pg进程即可复现,结合前一点,如果主库真的异常崩溃,很可能根本都无法启动
不幸中的万幸主库还活着,也存在以下风险:
- 主库数据丢失:未写入磁盘的事务可能会丢失,因为相关的WAL日志已经被删除。
- 数据不一致:如果已经提交的事务所对应的 WAL 日志被删除,那么这些事务的修改可能无法被正确地应用到从库上,从而导致数据不一致。
- 主从同步中断:相当于从库需要的日志被删除
- 旧备份无法恢复到最新状态:理论上建议立即发起一个全备,因为之前的备份链已经断了
来整点高危操作模拟一下,测试版本为pg 14
- 删除正在用的WAL日志,主库是否会crash
- 删除正在用的WAL日志,正常停库,能否启动
- 删除正在用的WAL日志,kill pg进程,能否启动
查看主库当前在用的wal日志
-bash-4.2$ pg_controldata
…
Latest checkpoint's REDO WAL file: 0000000500000008000000FD
试试将其删除会发生什么

可以看到pg进程还在(当然不保证每次都会还在)

造点数据执行checkpoint,会发现它重新生成了FD的文件,但其实原来文件里的数据已经丢失了。

挪走FD文件,尝试正常停库再启动

发现能起来,并且它切换到了用FE文件

挪走FE文件,kill pg进程
再启动,这下悲剧了

看看日志报错

这只能走点更极端的路子,把pg_wal给reset掉,影响当然就更大了
pg_resetwal -f /data/postgres/pg5432/data

② 从库需要的文件被删除
- 导致主从中断,由于pg原生不支持并行备份,大库的搭建耗时耗力。
- 如果主库生成的wal日志量非常大(例如每天TB级),那更是雪上加霜,甚至需要停主库业务来搭建。
③ 新特性
在pg 12中,新增了wal_recycle参数,可以关闭wal日志的重命名,并且不需重启生效。虽然引入的原因是在COW文件系统中新建日志会更快,但也在很大程度上规避了误删wal日志的风险。
2. 孤儿.ready文件
这部分特别感谢熊灿灿老师帮忙一起分析,又学到不少新知识:
小心!孤儿归档也可能将数据库整死!
① pg 12之前
如果你只删除wal日志,会导致其对应的.ready变成孤儿文件,归档进程归档失败,会反复尝试处理第一个失败的wal日志,最终的结果就是wal日志仍然无法归档,出现堆积,空间被占满。
以pg 10为例,相关代码如下。可以看到如果归档失败,它只会sleep一段时间然后重试,并不做额外处理,因此日志中会一直看到archiving write-ahead log file failed too many times, will try again later 的报错
static void
pgarch_ArchiverCopyLoop(void)
{char xlog[MAX_XFN_CHARS + 1];/** loop through all xlogs with archive_status of .ready and archive* them...mostly we expect this to be a single file, though it is possible* some backend will add files onto the list of those that need archiving* while we are still copying earlier archives*/while (pgarch_readyXlog(xlog)){int failures = 0;for (;;){/** Do not initiate any more archive commands after receiving* SIGTERM, nor after the postmaster has died unexpectedly. The* first condition is to try to keep from having init SIGKILL the* command, and the second is to avoid conflicts with another* archiver spawned by a newer postmaster.*/if (got_SIGTERM || !PostmasterIsAlive())return;/** Check for config update. This is so that we'll adopt a new* setting for archive_command as soon as possible, even if there* is a backlog of files to be archived.*/if (got_SIGHUP){got_SIGHUP = false;ProcessConfigFile(PGC_SIGHUP);}/* can't do anything if no command ... */if (!XLogArchiveCommandSet()){ereport(WARNING,(errmsg("archive_mode enabled, yet archive_command is not set")));return;}if (pgarch_archiveXlog(xlog)){/* successful */pgarch_archiveDone(xlog);/** Tell the collector about the WAL file that we successfully* archived*/pgstat_send_archiver(xlog, false);break; /* out of inner retry loop */}else{/** Tell the collector about the WAL file that we failed to* archive*/pgstat_send_archiver(xlog, true);if (++failures >= NUM_ARCHIVE_RETRIES){ereport(WARNING,(errmsg("archiving write-ahead log file \"%s\" failed too many times, will try again later",xlog)));return; /* give up archiving for now */}pg_usleep(1000000L); /* wait a bit before retrying */}}}
}
② 新特性
pg 12开始引入了一个检查,如果归档过程中发现仅有.ready文件,而没有对应wal日志,pg会在日志输出一条告警并删除这个.ready文件。
以pg 14为例,它新增了一段代码
if (stat(pathname, &stat_buf) != 0 && errno == ENOENT){char xlogready[MAXPGPATH];StatusFilePath(xlogready, xlog, ".ready");if (unlink(xlogready) == 0){ereport(WARNING,(errmsg("removed orphan archive status file \"%s\"",xlogready)));/* leave loop and move to the next status file */break;}if (++failures_orphan >= NUM_ORPHAN_CLEANUP_RETRIES){ereport(WARNING,(errmsg("removal of orphan archive status file \"%s\" failed too many times, will try again later",xlogready)));/* give up cleanup of orphan status files */return;}/* wait a bit before retrying */pg_usleep(1000000L);continue;
3. 孤儿wal文件会有什么效果
因为archive process的效率依赖于archive_status目录中的.ready文件数,当时还想了个办法——如果我不清wal日志,只把.ready mv走会怎么样?
事实证明是不可行的,pg检查到wal日志没有.ready文件后,会给它重新生成一个对应的.ready文件,然后开始给它做归档,相当于归档是正常进行的。
四、 如何相对安全地手动清理wal日志
1. 准备工作
① pg 12及以上版本
整体操作是类似的,但对于高版本pg,相当于还有两道保险操作
- 设置 wal_recycle 为off,调整不用重启,可以极大地避免误删除风险

- wal日志和.ready文件不要求完全对齐
虽说12版本开始pg会自动清理孤儿.ready文件,但当堆积量太大时,归档进程处理效率会极其低下,等它一个个报错清也要到猴年马月。不过好处是可以不必完全对齐,差个几十几百的,pg也能自己给你处理了。
② pg 12之前版本
这就得靠自己了,我们准备了以下几道保险:
- 与业务沟通停写入业务,尽量减少wal日志产生量。
- 查看主从库当前在用的wal日志,待清理日志必须小于这个日志号。
以下图为例,主库当前发送的日志是1759xxxx,那么删除时,我们最多只删1758xxx的日志号

- 以文件修改时间和文件名共同作为删除条件
当wal日志被重命名后,虽然ll -rth查看其时间未变,但stat命令可以看到其change time和modify time都是变化的。因此我们实际应该用ll -rcth排序,查看最旧的日志名。
理论上知道这个特点后,用pg_archivecleanup按文件名分批清理也是可以的,并且速度更快。但安全起见我们还是用find命令,以ctime,mtime,name条件共同限制
ll -crth |grep 0000000300174|more
ll -cth |grep 0000000300174|more
ll -cth |grep 0000000300174|wc -l# 输出结果
Jan 29 04:55 - 10:29 0000000300174*
- 输出待清理的文件名
find /data/postgres/pg5432/data/pg_wal -maxdepth 1 -mtime +0 -ctime +0 -name "0000000300174*" -exec ls -lhc {} \; > /tmp/old_wal_174.txt
检查输出的文件名及时间是否符合预期
2. 正式清理
- 业务停写入操作
- 停pgbackrest
pgbackrest stop
- 删除对应wal日志
find /data/postgres/pg5432/data/pg_wal -maxdepth 1 -mtime +0 -ctime +0 -name "0000000300174*" -exec rm -rf {} \;
- 挪走对应.ready文件
这步若是使用find+rm,在文件量大时耗时过长,例如24万个文件,find+rm耗时超过15分钟,而mv在1分钟内可完成。
mkdir -p /data/postgres/pg5432/data/pg_wal/archive_status_bak0129
cd /data/postgres/pg5432/data/pg_wal/archive_status
mv 0000000300174*ready ../archive_status_bak0129/
- 循环执行以上步骤,直至删完待清理文件
- 启动pgbackrest
pgbackrest start
ps -ef|grep pgbackrest
- 检查archiver process
应该开始处理剩余的wal日志并且逐步推进(正常每秒10个以上),没有报错
ps -ef|grep archiver
- 检查.ready文件数,应该逐渐减少
cd /data/postgres/pg5432/data/pg_wal/archive_status
ll -h |grep ready|wc -l
- 检查wal日志数,应该逐渐减少
cd /data/postgres/pg5432/data/pg_wal/
ll -h |wc -l
- 启动应用
一段时间后.ready文件及wal日志数应该能稳定在一个水平线而非持续走高,以我们的库为例,大概在7000个wal日志,120G左右
- 新增告警线
新加了wal目录大小的监控及告警,及时发现日志堆积问题,尽早处理
select sum((pg_stat_file(file)).size) from (select dir||'/'||pg_ls_dir(dir) as file from (select setting as dir from pg_settings where name='log_directory') t)t where (pg_stat_file(file)).change>=current_date;相关文章:
postgresql 手动清理wal日志的101个坑
新年的第一天,总结下去年遇到的关于WAL日志清理的101个坑,以及如何相对安全地进行清理。前面是关于WAL日志堆积的原因分析,清理相关可以直接看第三部分。 首先说明,手动清理wal日志是一个高风险的操作,尤其对于带主从的…...
【开源训练数据集3】Top3人脸数据集及其使用方法-计算机视觉应用
目录 什么是人脸数据集? Top 3 人脸数据集 CelebFaces Attributes (CelebA)数据集 Flickr-Faces-HQ (FFHQ) 数据集 野外标记面孔 (LFW) 使用先进的人脸数据集 CelebA 访问数据集 在 Pytorch 中使用 CelebA 在 Tensorflow 中使用 CelebA Flickr-Faces-HQ 数据集 (FFH…...
精灵图,字体图标,CSS3三角
精灵图 1.1为什么需要精灵图 一个网页中往往会应用很多小的背景图像作为修饰,当网页中的图像过多时,服务器就会频繁的接受和发送请求图片,造成服务器请求压力过大,这将大大降低页面的加载速度。 因此,为了有效地减少…...
.NET Core性能优化技巧
.NET Core作为一个跨平台的开源框架,以其高效、灵活和可扩展的特性受到了广大开发者的青睐。但在实际开发中,如何确保应用程序的性能始终是一个关键的问题。本文将介绍十大.NET Core性能优化技巧,帮助开发者提升应用程序的性能。 1. 使用异步…...
人类智能远远超越了物理与数理范畴
德国哲学家黑格尔曾这样写道,我们越是熟悉的东西,就越不清楚它。这或许意味着当我们对某个事物非常熟悉时,可能会陷入一种思维定势,导致我们无法客观地认识和理解它。这种思维定势可能来自于习惯、传统观念或者个人经验࿰…...
数据库管理-第149期 Oracle Vector DB AI-01(20240210)
数据库管理149期 2024-02-10 数据库管理-第149期 Oracle Vector DB & AI-01(20240210)1 机器学习2 向量3 向量嵌入4 向量检索5 向量数据库5 专用向量数据库的问题总结 数据库管理-第149期 Oracle Vector DB & AI-01(20240210…...
FlinkSql通用调优策略
历史文章迁移,稍后整理 使用DataGenerator 提前进行压测,了解数据的处理瓶颈、性能测试和消费能力 开启minibatch:"table.exec.mini-batch.enabled", "true" 开启LocalGlobal 两阶段聚合:"table.exec.m…...
Linux在云计算领域的重要作用
在云计算领域,Linux扮演着至关重要的角色。以下是Linux在云计算领域中的重要作用: 稳定性和安全性:Linux操作系统具有稳定性和安全性,可以有效地保护用户的数据安全。它具有各种安全功能,可以防止未经授权的访问&…...
sqlserver2012 解决日志大的问题 bat脚本
要解决SQL Server 2012中事务日志过大的问题,你可以创建一个批处理脚本(.bat)来定期备份事务日志。下面是一个示例批处理脚本,该脚本使用SQLCMD工具来执行事务日志备份: echo off set "DBNAMEYourDatabaseName&qu…...
SpringCloud之Eureka注册中心和负载均衡
SpringCloud之Eureka注册中心和负载均衡 微服务技术栈认识微服务单体架构分布式架构微服务 微服务拆分及远程调用微服务拆分注意事项 Eureka注册中心提供者与消费者原理分析服务调用出现的问题Eureka的作用 使用流程1、搭建EurekaServer2、注册user-service3、在order-service完…...
Python 数据可视化之山脊线图 Ridgeline Plots
文章目录 一、前言二、主要内容三、总结 🍉 CSDN 叶庭云:https://yetingyun.blog.csdn.net/ 一、前言 JoyPy 是一个基于 matplotlib pandas 的单功能 Python 包,它的唯一目的是绘制山脊线图 Joyplots(也称为 Ridgeline Plots&…...
VTK 三维场景的基本要素(相机) vtkCamera 相机的运动
相机的运动 当物体在处于静止位置时,相机可以在物体周围移动,摄取不同角度的图像 移动 移动分为相机的移动,和相机焦点的移动;移动改变了相机相对焦点的位置,离焦点更近或者更远;这样就会改变被渲染的物体…...
C++ //练习 6.53 说明下列每组声明中的第二条语句会产生什么影响,并指出哪些不合法(如果有的话)。
C Primer(第5版) 练习 6.53 练习 6.53 说明下列每组声明中的第二条语句会产生什么影响,并指出哪些不合法(如果有的话)。 (a) int calc(int &, int &);int calc(const int &, const int &); (b) int …...
缓慢变化维 常用的处理方法
什么是缓慢变化维 维度 在数仓中,表往往会被划分成两种类型,一种是 事实表,另一种是维度表,举个例子,比如说: ❝ 2024年2月14日,健鑫在12306上买了两张火车票,每张火车票400元&…...
free pascal:fpwebview 组件通过JSBridge调用本机TTS
从 https://github.com/PierceNg/fpwebview 下载 fpwebview-master.zip 简单易用。 先请看 \fpwebview-master\README.md cd \lazarus\projects\fpwebview-master\demo\js_bidir 学习 js_bidir.lpr ,编写 js_bind_speak.lpr 如下,通过JSBridge调用本机…...
C语言静态库深入剖析
在C语言编程实践中,库是代码复用和模块化开发的重要基础结构。静态库作为其中一种主要的库类型,其内容在编译链接阶段即被完整地嵌入到最终生成的可执行文件中,从而使得程序在运行时无需外部依赖。本篇博客将系统性、详细地剖析C语言静态库的…...
A股上市以来涨幅排行榜
一、统计数据说明 1. 涨幅排行榜是根据股价的后复权价格计算的,该价格考虑了分红送股拆股等事件对股价的影响,相当于是分红再投资的股价。 2. 年化投资收益率,是根据IPO收盘价至今涨幅计算的复合年化收益率。例如,假设一个股票上…...
鸿蒙开发系列教程(十八)--页面内动画(1)
页面内的动画 显示动画 语法:animateTo(value: AnimateParam, event: () > void): void 第一个参数指定动画参数 第二个参数为动画的闭包函数。 如:animateTo({ duration: 1000, curve: Curve.EaseInOut }, () > {动画代码}) dura…...
Web基础01-HTML+CSS
目录 一、HTML 1.概述 2.html结构解析 3.HTML标签分类 4.HTML标签关系 5.HTML空元素 6.HTML属性 7.常用标签 (1)HTML标签 (2)标题标签 (3)换/折行标签 (4)段落标签 &am…...
Linux命令行全景指南:从入门到实践,掌握命令行的力量
目录 知识梳理思维导图: linux命令入门 为什么要学Linux命令 什么是终端 什么是命令 关于Linux命令的语法 tab键补全 关于命令提示符 特殊目录 常见重要目录 /opt /home /root /etc /var/log/ man命令 shutdown命令 history命令 which命令 bash…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
线程同步:确保多线程程序的安全与高效!
全文目录: 开篇语前序前言第一部分:线程同步的概念与问题1.1 线程同步的概念1.2 线程同步的问题1.3 线程同步的解决方案 第二部分:synchronized关键字的使用2.1 使用 synchronized修饰方法2.2 使用 synchronized修饰代码块 第三部分ÿ…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
React19源码系列之 事件插件系统
事件类别 事件类型 定义 文档 Event Event 接口表示在 EventTarget 上出现的事件。 Event - Web API | MDN UIEvent UIEvent 接口表示简单的用户界面事件。 UIEvent - Web API | MDN KeyboardEvent KeyboardEvent 对象描述了用户与键盘的交互。 KeyboardEvent - Web…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
