首页 > 数据库 >PostgreSQL数据库WAL日志空间大小以及不清理的原因深入分析

PostgreSQL数据库WAL日志空间大小以及不清理的原因深入分析

时间:2023-09-26 15:22:56浏览次数:46  
标签:wal PostgreSQL max WAL 复制 日志 size

1. 背景
很多初学者会对WAL日志占用多少空间比较疑惑,听网上的一些文章说是由max_wal_size来控制的,但发现很多时候WAL日志空间会超过这个设置的值,不知道为什么? 同时有时会发现WAL日志不清理了,占用空间在不停的增长,然后不知道为什么?看一些网上的文章,发现情况不是网上说的那种情况。中启乘数科技工程师在服务客户的工程师,遇到了导致WAL日志空间膨胀不清理的各种用分期,并进行了深入全面的分析,基本囊括了所有的导致WAL日志膨胀的各种原因。所以对于初学者来说不需要再看网上那些不全面的文章了,只看这篇文章就够了。


2. 决定WAL日志占用空间大小因素
控制WAL日志的数量由以下这三个参数控制:

  • max_wal_size
  • min_wal_size
  • wal_keep_segments或wal_keep_size

注意:PostgreSQL13版本后,wal_keep_segments参数以及废弃了,由wal_keep_size替代此参数
很多人认为WAL占用的空间是由max_wal_size来控制的,这种认识是不全面的,下面我们详细讲解这几个参数的意思。
假设pg_wal下的文件为:

000000A7000000040000005A
000000A7000000040000005B
000000A7000000040000005C
000000A7000000040000005D
000000A7000000040000005E
000000A7000000040000005F
000000A70000000400000060
000000A70000000400000061
000000A70000000400000062
000000A70000000400000063
000000A70000000400000064

假设当前正在写的WAL文件为000000A70000000400000060,则wal_keep_segments控制000000A7000000040000005A到000000A70000000400000060的个数,
而min_wal_size控制000000A70000000400000060到000000A70000000400000064,即这一段至少要保留min_wal_size的WAL日志。
如果min_wal_size + wal_keep_segments 大于了max_wal_size,那么WAL日志空间至少也会占用:min_wal_size + wal_keep_segments。所以从这里可以看出,WAL占用的空间大小并不是完全由max_wal_size控制的,只有在min_wal_size + wal_keep_segments的值小于max_wal_size时,PostgreSQL才尽量保值WAL的空间不超过这个值。注意这里说的是尽量,原因是PostgreSQL是在做checkpoint时,把不需要的WAL日志给清理掉,但是如果数据库由很大的写,导致还没有来得及做checkpoint时,这时WAL日志占用的空间会超过max_wal_size设置的值。
如果min_wal_size + wal_keep_segments小于max_wal_size,那么WAL日志空间尽量保持不超过max_wal_size参数设置的值,当然每次checkpoint清理时,会保持WAL的日志空间不会低于min_wal_size + wal_keep_segments的值。
所以从这个原理来说:min_wal_size不需要设置太大,生产库只需要为1G左右大小时就够用了,不需要太大。
而为了防止备库同步失败,应该设置一个较大的wal_keep_segments,WAL文件为16M大小,可把wal_keep_segments设置为500或更大。
max_wal_size比 min_wal_size + wal_keep_segments略大一点就可以了。
实际上参数max_wal_size主要时为了控制checkpoint发生的频繁程度:

target = (double) ConvertToXSegs(max_wal_size_mb) / (2.0 + CheckPointCompletionTarget);

如果checkpoint_completion_target设置为0.5时,则每写了 max_wal_size/2.5 的WAL日志时,就会发送一次checkpoint。
checkpoint_completion_target的范围为0~1,那么结果就是写的WAL的日志量超过: max_wal_size的1/3~1/2时,就会发生一次checkpoint。


3. 导致WAL日志空间膨胀的原因
3.1 长事务
数据库中如果有长事务,PostgreSQL数据库对于这个长事务开始后产生的所有WAL日志都不会清理。
select pid,usename, xact_start from pg_stat_activity where now() - xact_start > interval ‘8 hours’;
下面时监控超过8个小时的长事务的SQL:

select pid,usename, xact_start from pg_stat_activity where now() - xact_start > interval '8 hours';

更甚的情况是用户有“Idle in transaction”的连接,即一个连接开启了事务,然后什么事情也不干,一直空闲着,用下面的SQL查询“Idle in transaction”的连接:

select pid,client_addr,usename,datname, xact_start,state from pg_stat_activity where state not in ('active','idle') order by  xact_start;

如果有长时间的“idle in transaction”的连接,需要kill掉,kill的方法是select pg_terminate_backend(3415),其中3415是这个连接的pid。当然kill掉之前需要调查这中长时间的“idle in transaction”的连接是如何产生的。
对于一些应用产生的“idle in transaction”随便kill掉可能会导致应用出现问题,需要注意


3.2 废弃的复制槽(replication slots)
复制槽是用来保证逻辑复制或物理复制需要的WAL日志不会被清理掉。如果使用了逻辑复制或物理复制使用的复制槽,而这些逻辑复制或物理因为某些原因停掉了,那么会导致这些复制槽会把WAL的日志保留着。如果是逻辑复制或物理复制停掉了,则需要尽快把这些逻辑复制或物理复制启动起来,否则很容易把主库的空间撑满。
用下面的SQL查询复制槽:

SELECT slot_name, slot_type, database, xmin,active,active_pid FROM pg_replication_slots ORDER BY age(xmin) DESC;

如果上面结果某一行中active为空,说明复制停掉了,需要检查。
如果逻辑复制或物理复制停掉了,但一时半会还启动不起来,而主库的空间又要慢了,这时可以强制把复制槽给删除掉,注意删除掉逻辑复制的复制槽后,逻辑复制的同步就废弃了,后续的恢复需要做全量的数据恢复。所以这是逻辑复制的一个大缺点。逻辑复制还有一个大缺点是主备库切换后,逻辑复制槽也废掉了。如果想避免这个问题,可以使用中启乘数科技的产品CMiner,具体请见CMiner介绍页面


3.3 废弃的未提交两阶段事务(prepared transactions)
未提交的两阶段事务(prepared transactions),会让数据库保留从这个事务开始时WAL日志,导致WAL日志空间膨胀。如果应用使用了两阶段事务,理论上两阶段事务的提交和回滚时需要由这个应用来提交或回滚的,而如果这个应用出现的问题,一直没有对其创建的两阶段事务进行提交或回滚,则会产生此问题。
查询两阶段事务的语句:

SELECT gid, prepared, owner, database, transaction AS xmin
FROM pg_prepared_xacts
ORDER BY age(transaction) DESC;

如果发现某个两阶段事务长期存在(如数个小时),则可能出现了这个问题,如下所示:

postgres=# SELECT gid, prepared, owner, database, transaction AS xmin
FROM pg_prepared_xacts
ORDER BY age(transaction) DESC;
    gid     |           prepared            |  owner   | database | xmin
------------+-------------------------------+----------+----------+-------
 osdba_pxid | 2019-01-10 10:27:15.441513+08 | codetest | postgres | 13843
(1 row)

如果发现prepared列的时间是一个之前很久的时间,基本可以断定这是一个废弃的两阶端事务。这时我们可以手工提交或回滚这个事务:
提交的方法:

commit prepared 'osdba_pxid';

回滚的方法:

roback prepared 'osdba_pxid';

注意需要调查两阶端事务产生的原因以及确定应该是提交还是回滚,否则可能造出数据的丢失。


3.4 主库的WAL日志的归档未成功
主库不会清理未归档的WAL日志,从而导致了主库的WAL日志膨胀。
主库开启了归档,但是归档命令一直没有执行成功,或归档命令hang住,也可能是归档命令执行的太慢,来不及归档。
检查主库的日志,看看释放又归档失败的日志。也可以到pg_wal/archive_status目录下,看看是否大量的WAL日志未归档成功。


3.5 备库开启的HOT_STANDBY_FEEDBACK
如果只读备库开启了HOT_STANDBY_FEEDBACK,备库上如果有个长时间运行的查询正在执行,备库会通知主库这个备库上长时间查询开始启动后的WAL日志都不能被清理掉,从而导致主库的WAL日志膨胀。这种情况导致主库WAL日志膨胀出现的概率很低。
有人问为什么要有HOT_STANDBY_FEEDBACK这种机制呢?
原因是如果没有这种机制,主库执行UPDATE并VACUUM了,由于主库上已经不存在使用被更新元组的事务,VACUUM 会将这些元组清理掉,当 备库回放到 VACUUM 对应的日志时,检测到当前 VACUUM 清理的元组仍然被这个长时间的查询使用,则会阻塞备库的WAL日志应用,导致备库有很大的延迟。为了避免备库的延迟,PostgreSQL又提供了参数max_standby_streaming_delay(默认30s),让应用WAL的进程在等待此参数指定的时间后后,若长时间SQL还没有执行完,则直接取消长时间SQL的运行,并在日志种打印如下异常信息:

FATAL: terminating connection due to conflict with recovery  
DETAIL: User query might have needed to see row versions that must be removed. 
HINT: In a moment you should be able to reconnect to the database and repeat your command. 
server closed the connection unexpectedly  
 This probably means the server terminated abnormally  
        before or while processing the request. 
The connection to the server was lost. Attempting reset: Succeeded.

那么这样就导致了备库上无法运行长时间的SQL。为了解决此问题,备库把参数HOT_STANDBY_FEEDBACK设置为on后,就将 备库种长时间运行的SQL的最小活跃事务ID定期告知主库,使得主库在执行 VACUUM时对这些事务还需要的数据手下留情,不进行清理。

转载自:https://zhuanlan.zhihu.com/p/622585294

标签:wal,PostgreSQL,max,WAL,复制,日志,size
From: https://www.cnblogs.com/lovezhr/p/17730163.html

相关文章

  • alert日志中出现大量“WARNING too many parse errors”告警
    1、一套19.19的ORACLE数据库,alert日志中出现大量的parseerrors告警信息,具体如下所示。WARNING:toomanyparseerrors,count=9239SQLhash=0x5da2e911PARSEERROR:ospid=51405,error=923forstatement:Additionalinformation:hd=0x4b789d8b0phd=0x4ae3cd7e0flg=......
  • 【开发/调试工具】【串口工具】不同串口软件如何生成带时间戳的日志
    https://blog.csdn.net/qxhgd/article/details/126152913 Xshell在新建会话属性页面,可配置日志的日期格式:  IPOP可根据需要调整标签间隔时间: ......
  • nginx日志分析: 每小时请求量最高的IP
    按小时来统计,当前指定日志的每小时最高流量的前10个IP,并显示出请求的状态码.nginx.conf中配置的日志格式为:  log_format main '$remote_addr-$remote_user[$time_local]"$request"'           '$status$body_bytes_sent"$http_refere......
  • Linux CentOS 7.x离线安装PostgreSQL操作手册
    一、准备环节rpm-qa|greppostgres检查PostgreSQL是否已经安装rpm-qal|greppostgres检查PostgreSQL安装位置postgresql-12.2.tar.gz二、Pgsql数据库安装下载下载地址:http://www.postgresql.org/ftp/source/选择你你需要的版本,本次安装12.2的版......
  • 如何阻止os.walk遍历所有子目录?
    果只想搜索指定的目录。最好使用os.listdir,然后只过滤os.path.isfile,如下所示:importosmy_path='/entire/path/to/files/'file_list=[]forfilenameinos.listdir(my_path):filepath=os.path.join(my_path,filename)ifos.path.isfile(filepath):fileList.a......
  • apache日志类型及作用
    apache标准中规定了4类日志: 错误日志 访问日志 传输日志 Cookie日志 其中:传输日志和Cookie日志被Apache2.0认为已经过时,同时错误日志和访问日志被Apache2.0+默认设置 访问日志 访问服务器的远程机器的地址:可以得知浏览者来自何方 浏览者访问的资源:可以得知......
  • PostgreSQL Serial
    概念描述PostgreSQL中的SERIAL是一种特殊的类型,用于创建自增长的整数列,通常用作表的主键或其他需要唯一标识的列。SERIAL实际上不是真正的类型,而是一种便捷的写法,它会自动创建一个SEQUENCE对象,并将该SEQUENCE的下一个值作为该列的默认值。PostgreSQLSERIAL是一种特殊的用于生产整......
  • 日志模块
    1.日志模块的基本使用"""日志的级别:后续我们写日志的时候,可以按照日志的级别选择性的记录"""logging.debug('debugmessage') #10logging.info('infomessage') #20logging.warning('warningmessage') #30logging.error('erro......
  • pytest + yaml 框架 -56. 输出日志优化+allure报告优化
    前言v1.4.8版本优化接口请求和响应输出日志,生成的allure报告也按步骤优化request和response详情日志优化日志用例test_log1:-name:log1request:url:http://127.0.0.1:8000/api/test/demomethod:GETvalidate:-eq:[status_code,200]-eq:......
  • debian 安装包中changelog.Debian 日志格式
     官方说明文档: https://www.debian.org/doc/debian-policy/index.html#contents A、debian/changelog应该简单就介绍安装包的版本,当然也可以包含与上一个版本的些改变。changelog应该能够使安装工具(例如:dpkg)去发现安装包的版本号和其他的release信息。changelog文件的......