Postgresql WAL日志浅析
一、预写日志(WAL)
预写式日志(Write Ahead Log,WAL)是保证数据完整性的一种标准方法。简单来说,WAL的中心概念是数据文件(存储着表和索引)的修改必须在这些动作被日志记录之后才被写入,即在描述这些改变的日志记录被刷到持久存储以后。如果我们遵循这种过程,我们不需要在每个事务提交时刷写数据页面到磁盘,因为我们知道在发生崩溃时可以使用日志来恢复数据库:任何还没有被应用到数据页面的改变可以根据其日志记录重做(这是前滚恢复,也被称为REDO)。
使用WAL可以显著降低磁盘的写次数,因为只有日志文件需要被刷出到磁盘以保证事务被提交,而被事务改变的每一个数据文件则不必被刷出。日志文件被按照顺序写入,因此同步日志的代价要远低于刷写数据页面的代价。在处理很多影响数据存储不同部分的小事务的服务器上这一点尤其明显。此外,当服务器在处理很多小的并行事务时,日志文件的一个fsync可以提交很多事务。关闭 fsync 对 SELECT 无影响, 而 UPDATE 性能有较大提升,这个场景提升了 111%;当然关闭 fsync 参数的代价是巨大的,当数据库主机遭受操作系统故障或硬件故障时,数据库很有可能无法启动,并丢失数据,建议生产库不要关闭这参数。
WAL也使得在线备份和时间点恢复能被支持。通过归档WAL数据,我们可以支持回转到被可用WAL数据覆盖的任何时间:我们简单地安装数据库的一个较早的物理备份,并且重放WAL日志一直到所期望的时间。另外,该物理备份不需要是数据库状态的一个一致的快照 — 如果它的制作经过了一段时间,则重放这一段时间的WAL日志将会修复任何内部不一致性。
二、简介
类似于Oracle的redo,PostgreSQL的redo文件被称为WAL文件或XLOG文件,存放在 $PGDATA/pg_xlog或 $PGDATA/pg_wal目录中(PostgreSQL从10版本开始,将所用xlog相关的全部用wal替换了)。任何试图修改数据库数据的操作都会写一份日志到磁盘。wal命名格式文件名称为16进制的24个字符组成,每8个字符一组,每组的意义如下:
通过select pg_switch_xlog()或select pg_switch_wal();可以切换xlog/wal日志。
wal日志即write ahead log预写式日志,简称wal日志。wal日志可以说是PostgreSQL中十分重要的部分,相当于oracle中的redo日志。 当数据库中数据发生变更时: change发生时:先要将变更后内容计入wal buffer中,再将变更后的数据写入data buffer; commit发生时:wal buffer中数据刷新到磁盘; checkpoint发生时:将所有data buffer刷新的磁盘。
如果没有wal日志,那么数据库中将会发生什么? 首先,当我们在数据库中更新数据时,如果没有wal日志,那么每次更新都会将数据刷到磁盘上,并且这个动作是随机i/o,性能可想而知。并且没有wal日志,关系型数据库中事务的ACID如何保证呢? 因此wal日志重要性可想而知。其中心思想就是:先写入日志文件,再写入数据。
说到checkpoint,我们再来看看哪些情况会触发数据库的checkpoing: 1.手动执行CHECKPOINT命令; 2.执行需要检查点的命令(例如pg_start_backup 或pg_ctl stop|restart等等); 3.达到检查点配置时间(checkpoint_timeout); 4.max_wal_size已满。
checkpoint_timeout: 自动 WAL 检查点之间的最长时间,以秒计。合理的范围在 30 秒到 1 天之间。默认是 5 分钟(5min)。增加这个参数的值会增加崩溃恢复所需的时间。
max_wal_size: 在自动 WAL检查点之间允许WAL 增长到的最大尺寸。这是一个软限制,在特殊的情况 下 WAL 尺寸可能会超过max_wal_size, 例如在重度负荷下、archive_command失败或者高的 wal_keep_segments设置。默认为 1 GB。增加这个参数可能导致崩溃恢复所需的时间。( wal_keep_segments用于指定pg_wal目录中保存的过去的wal文件(wal 段)的最小数量,以防备用服务器在进行流复制时需要。)
和max_wal_size相对应的还有个min_wal_size,只要 WAL 磁盘用量保持在这个设置之下,在检查点时旧的 WAL文件总是被回收以便未来使用,而不是直接被删除。
wal切换步骤是这样的:单个wal日志写满(默认大小16MB,编译数据库时指定)继续写下一个wal日志,直到磁盘剩余空间不足min_wal_size时才会将旧的 WAL文件回收以便继续使用。但是这种模式有一个弊端就是如果在checkpoint之前产生了大量的wal日志就会导致发生checkpoint时对性能的影响巨大,因此pg中还有一个参数checkpoint_completion_target 来进行调整。
checkpoint_completion_target: 指定检查点完成的目标,作为检查点之间总时间的一部分。默认是 0.5。假如我的checkpoint_timeout设置是30分钟,而wal生成了10G,那么设置成0.5就允许我在15分钟内完成checkpoint,调大这个值就可以降低checkpoint对性能的影响,但是万一数据库出现故障,那么这个值设置越大数据就越危险。
总结:大多数检查点应该是基于时间的,即由checkpoint_timeout触发。 性能(不频繁检查点)与恢复所需时间(频繁检查点)之间需要抉择: 值在15-30分钟之间是比例合适的,但到1小时不是什么坏事。 在决定checkpoint_timeout后,通过估计WAL的数量选择max_wal_size。 设置checkpoint_completion_target以便内核将数据刷新到磁盘的时间足够(但不是太多)
三、切换WAL日志
pg_switch_wal()或pg_switch_xlog()强制服务器切换到一个新的预写式日志文件,这允许对当前文件进行归档(假设你正在使用连续归档)。 其结果是在刚刚完成的预写式日志文件中结束预写式日志位置加1。 如果自从上次预写式日志切换以来没有提前写日志活动,pg_switch_wal将不做任何操作,并返回当前正在使用的提前写日志文件的起始位置。默认情况下该函数仅限超级用户使用,但可以授权其他用户执行该函数。pg_switch_xlog()用于PG 10之前,从PG 10开始切换归档日志使用pg_switch_wal()。PG也提供了相应的函数根据LSN获取日志文件名:
四、配置WAL归档

必须重启,如果开启了归档,则在归档路径下的archive_status目录里,会有类似000000010000000000000002.ready和000000010000000000000003.done的文件,.ready表示XLOG文件已写满,可以调用归档命令了,.done表示已归档完成。开启了归档后,只有归档成功的pg_xlog文件才会被清除。在每次归档命令被执行后,会触发清除标签的动作,在执行检查点时,也会触发清除归档标签文件的动作。
五、自动清理WAL日志
一般来说,设置自动清理archive_log 可以在配置文件中添加archive_cleanup_command = 'pg_archivecleanup archivelocation %r'
或者:alter system set archive_cleanup_command='pg_archivecleanup /var/lib/postgresql/data/pg_wal %r';
六、手动清理WAL日志
如果配置了archive_mode=on,但是没有配置archive_command,那么xlog文件会一直堆积(xlog写完后,会写.ready,但是由于没有配置archive_command,也就是说不会触发归档命令,所以一直都不会写 .done),所以xlog会一直不清理。
1、读取控制文件,找到能清理的xlog范围

其中,“Latest checkpoint's REDO WAL file: 000000010000000000000005”之前的文件都可以清理。
2、通过pg_archivecleanup清理
pg_archivecleanup -d $PGDATA/pg_wal 000000010000000000000005
pg的wal日志保存在数据库目录下的pg_wal/子目录,这个日志存在的目的是为了保证崩溃后的安全,如果系统崩溃,可以“重放”从最后一次检查点以来的日志项来恢复数据库的一致性。但是也存在日志膨胀的问题,
七、WAL归档
在生产环境,为了保证数据高可用性,通常需要设置归档,所谓的归档,其实就是把pg_wal里面的日志备份出来,当系统故障后可以通过归档的日志文件对数据进行恢复:
配置归档需要开启如下参数:
1、wal_level = replica (pg13默认已经开启replica) 该参数的可选的值有minimal,replica和logical,wal的级别依次增高,在wal的信息也越多。由于minimal这一级别的wal不包含从基础的备份和wal日志重建数据的足够信息,在该模式下,无法开启wal日志归档。
2、archive_mode = on 上述参数为on,表示打开归档备份,可选的参数为on,off,always 默认值为off,所以要手动打开。
3、archive_command = 'test ! -f /mnt/server/archivedir/%f && cp %p /mnt/server/archivedir/%f' 该参数的默认值是一个空字符串,他的值可以是一条shell命令或者一个复杂的shell脚本。在shell脚本或命令中可以用 “%p” 表示将要归档的wal文件包含完整路径的信息的文件名,用“%f” 代表不包含路径信息的wal文件的文件名。
注意:wal_level和archive_mode参数修改都需要重新启动数据库才可以生效。而修改archive_command则不需要。所以一般配置新系统时,无论当时是否需要归档,这要建议将这两个参数开启。
八、如何计算WAL文件名
我们知道由三部分组成,那么又是如何计算呢?公式如下:
WAL segment file name = timelineId +(uint32)LSN−1 / (16M ∗ 256) + (uint32)(LSN − 1 / 16M) % 256
示例:
1、查看当前LSN位置

查看当前wal: select pg_walfile_name(pg_current_wal_lsn());
2、这里的LSN是’ 1/C469AA30’ 我们转换为十进制数:

3、利用公式计算:

算出来的值与通过函数查询的一致:

九、计算WAL日志的大小

10、查看wal生成频率和大小
–wal 文件生成数量
–linux ls --full-time stat filename
–pg_stat_file返回一个记录,其中包含
– 1 size 文件尺寸
– 2 access 最后访问时间戳(linux:最近访问) 、
– 3 modification 最后修改时间戳(linux:最近更改–) 、
– 4 change 最后文件状态改变时间戳(只支持 Unix 平台)(linux:最近改动) 、
– 5 creation 文件创建时间戳(只支持 Windows)
– 6 isdir 一个boolean指示它是否为目录 isdir
with tmp_file as ( select t1.file, t1.file_ls, (pg_stat_file(t1.file)).size as size, (pg_stat_file(t1.file)).access as access, (pg_stat_file(t1.file)).modification as last_update_time, (pg_stat_file(t1.file)).change as change, (pg_stat_file(t1.file)).creation as creation, (pg_stat_file(t1.file)).isdir as isdir from (select dir||'/'||pg_ls_dir(t0.dir) as file, pg_ls_dir(t0.dir) as file_ls from ( select '/pg13/pgdata/pg_wal'::text as dir ) t0 ) t1 where 1=1 order by (pg_stat_file(file)).modification desc ) select to_char(date_trunc('day',tf0.last_update_time),'yyyymmdd') as day_id, sum(case when date_part('hour',tf0.last_update_time) >=0 and date_part('hour',tf0.last_update_time) <24 then 1 else 0 end) as wal_num_all, sum(case when date_part('hour',tf0.last_update_time) >=0 and date_part('hour',tf0.last_update_time) <1 then 1 else 0 end) as wal_num_00_01, sum(case when date_part('hour',tf0.last_update_time) >=1 and date_part('hour',tf0.last_update_time) <2 then 1 else 0 end) as wal_num_01_02, sum(case when date_part('hour',tf0.last_update_time) >=2 and date_part('hour',tf0.last_update_time) <3 then 1 else 0 end) as wal_num_02_03, sum(case when date_part('hour',tf0.last_update_time) >=3 and date_part('hour',tf0.last_update_time) <4 then 1 else 0 end) as wal_num_03_04, sum(case when date_part('hour',tf0.last_update_time) >=4 and date_part('hour',tf0.last_update_time) <5 then 1 else 0 end) as wal_num_04_05, sum(case when date_part('hour',tf0.last_update_time) >=5 and date_part('hour',tf0.last_update_time) <6 then 1 else 0 end) as wal_num_05_06, sum(case when date_part('hour',tf0.last_update_time) >=6 and date_part('hour',tf0.last_update_time) <7 then 1 else 0 end) as wal_num_06_07, sum(case when date_part('hour',tf0.last_update_time) >=7 and date_part('hour',tf0.last_update_time) <8 then 1 else 0 end) as wal_num_07_08, sum(case when date_part('hour',tf0.last_update_time) >=8 and date_part('hour',tf0.last_update_time) <9 then 1 else 0 end) as wal_num_08_09, sum(case when date_part('hour',tf0.last_update_time) >=9 and date_part('hour',tf0.last_update_time) <10 then 1 else 0 end) as wal_num_09_10, sum(case when date_part('hour',tf0.last_update_time) >=10 and date_part('hour',tf0.last_update_time) <11 then 1 else 0 end) as wal_num_10_11, sum(case when date_part('hour',tf0.last_update_time) >=11 and date_part('hour',tf0.last_update_time) <12 then 1 else 0 end) as wal_num_11_12, sum(case when date_part('hour',tf0.last_update_time) >=12 and date_part('hour',tf0.last_update_time) <13 then 1 else 0 end) as wal_num_12_13, sum(case when date_part('hour',tf0.last_update_time) >=13 and date_part('hour',tf0.last_update_time) <14 then 1 else 0 end) as wal_num_13_14, sum(case when date_part('hour',tf0.last_update_time) >=14 and date_part('hour',tf0.last_update_time) <15 then 1 else 0 end) as wal_num_14_15, sum(case when date_part('hour',tf0.last_update_time) >=15 and date_part('hour',tf0.last_update_time) <16 then 1 else 0 end) as wal_num_15_16, sum(case when date_part('hour',tf0.last_update_time) >=16 and date_part('hour',tf0.last_update_time) <17 then 1 else 0 end) as wal_num_16_17, sum(case when date_part('hour',tf0.last_update_time) >=17 and date_part('hour',tf0.last_update_time) <18 then 1 else 0 end) as wal_num_17_18, sum(case when date_part('hour',tf0.last_update_time) >=18 and date_part('hour',tf0.last_update_time) <19 then 1 else 0 end) as wal_num_18_19, sum(case when date_part('hour',tf0.last_update_time) >=19 and date_part('hour',tf0.last_update_time) <20 then 1 else 0 end) as wal_num_19_20, sum(case when date_part('hour',tf0.last_update_time) >=20 and date_part('hour',tf0.last_update_time) <21 then 1 else 0 end) as wal_num_20_21, sum(case when date_part('hour',tf0.last_update_time) >=21 and date_part('hour',tf0.last_update_time) <22 then 1 else 0 end) as wal_num_21_22, sum(case when date_part('hour',tf0.last_update_time) >=22 and date_part('hour',tf0.last_update_time) <23 then 1 else 0 end) as wal_num_22_23, sum(case when date_part('hour',tf0.last_update_time) >=23 and date_part('hour',tf0.last_update_time) <24 then 1 else 0 end) as wal_num_23_24 from tmp_file tf0 where 1=1 and tf0.file_ls not in ('archive_status') group by to_char(date_trunc('day',tf0.last_update_time),'yyyymmdd') order by to_char(date_trunc('day',tf0.last_update_time),'yyyymmdd') desc ;
Postgresql WAL日志浅析的更多相关文章
- PostgreSQL WAL日志详解
wal日志即write ahead log预写式日志,简称wal日志.wal日志可以说是PostgreSQL中十分重要的部分,相当于oracle中的redo日志. 当数据库中数据发生变更时:chang ...
- postgresql如何维护WAL日志/归档日志
WAL日志介绍 wal全称是write ahead log,是postgresql中的online redo log,是为了保证数据库中数据的一致性和事务的完整性.而在PostgreSQL 7中引入的 ...
- PostgreSQL日志号LSN和wal日志文件简记
弄明白日志号的原理之后,一段时间又有点忘记了,干脆整理一遍: (一)wal文件命名规则 1)在$PGDATA目录下面的pg_xlog目录中存放着xlog日志文件(10.1之后变为了pg_wal): t ...
- PostgreSQL归档日志 手动触发归档 select pg_switch_xlog()
(转,做记录) 一 环境信息--1.1 PostgreSQL 版本 9.3.0 --1.2 postgresql.conf wal_level = hot_standbyarchive_mode = ...
- WAL日志文件名称格式详解
转自:http://blog.osdba.net/534.html WAL日志文件名称格式详解 PostgreSQL的WAL日志文件在pg_xlog目录下,一般情况下,每个文件为16M大小: osdb ...
- PgSQL · 追根究底 · WAL日志空间的意外增长
问题出现 我们在线上巡检中发现,一个实例的pg_xlog目录,增长到4G,很是疑惑.刚开始怀疑是日志归档过慢,日志堆积在pg_xlog目录下面,未被清除导致.于是检查归档目录下的文件,内容如下.但发现 ...
- 主库的wal日志已经被归档或异常丢失如何搭建从库
关键字:wal日志归档 搭建从库 restore_command master 194.1 slave 194.4 wal归档目录 /backup/pgsql/pg_arch/ xlog目录 / ...
- PostgreSQL的日志文件介绍
PostgreSQL的日志文件 pg_log:数据库活动日志(也就是数据库的操作日志): pg_xlog:事务日志: pg_clog:事务状态日志(pg_clog是pg_xlog的辅助日志). 现在主 ...
- KingbaseES通过sys_waldump解析wal日志
前言 oracle中的redo日志我们无法直接读取,然而对于KingbaseES数据库,我们可以利用sys_waldump工具解析wal日志,查看wal日志记录的信息. 我们可以利用 sys_wald ...
- KingbaseES V8R3集群管理和维护案例之---failover切换wal日志变化分析
案例说明: 本案例通过对KingbaseES V8R3集群failover切换过程进行观察,分析了主备库切换后wal日志的变化,对应用者了解KingbaseES V8R3(R6) failover ...
随机推荐
- python文件名解析---从文件名获得分类类别
python文件名解析-从文件名获得分类类别 python os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表.列表以字母顺序. listdir()方法 举例说明 lis ...
- 7-3 停车场管理 (20point(s))
设有一个可以停放n辆汽车的狭长停车场,它只有一个大门可以供车辆进出.车辆按到达停车场时间的先后次序依次从停车场最里面向大门口处停放 (即最先到达的第一辆车停放在停车场的最里面) .如果停车场已放满n辆 ...
- Django静态文件配置(from表单-request对象方法-get请求与post请求-视图函数书写)
目录 一:静态文件配置 1.静态文件配置 2.什么是静态文件? 3.静态文件的创建 4.解决使用django后端代码修改前端不显示(缓存问题) 5.settings.py 静态文件配置 6.静态文接口 ...
- 如何用3D流体实现逼真水流效果?
华为应用市场在2022年HDC大会期间发布了一款3D水流主题,基于华为HMS Core Scene Kit服务能力,展现立体灵动的水流岛屿,可跟随用户指尖实现实时流体波动效果,既趣味又解压. 让变幻莫 ...
- 使用Logstash工具导入sqlserver数据到elasticSearch及elk分布式日志中心
首先记下这个笔记,Logstash工具导入sqlserver数据到elasticSearch. 因为logstash使用java写的,我本地开发是win11,所以javade jdk必须要安装.具体安 ...
- 8KB的C#贪吃蛇游戏热点答疑和.NET7版本
在之前的一篇文章<看我是如何用C#编写一个小于8KB的贪吃蛇游戏>中,介绍了在.NET Core 3.0的环境下如何将贪吃蛇游戏降低到8KB.不过也有很多小伙伴提出了一些疑问和看法,主要是 ...
- 突如其来的&quot;中断异常&quot;,我(Java)该如何处理?
# **一.何为异常?** ## 1.生活中的实例 生活中存在许多不正常: 上班路上自行车掉链子 上厕所手机掉马桶 下班回家钥匙丢失 ....... 2.程序中的实例 我们的代码中也许存在许多纰漏,导 ...
- flutter 2.x运行flutter run 报错Cannot run with sound null safety, because the following dependenciesdon'
flutter 2.x运行flutter run 报错Cannot run with sound null safety, because the following dependenciesdon' ...
- odoo资料
https://www.cnblogs.com/smarttony/category/1484288.html
- docker配置阿里云加速
登录阿里云找到镜像加速 搜索容器镜像服务,进入管理控制台 找到镜像加速器,根据下面配置好即可 centos7.7x64例子 sudo mkdir -p /etc/docker sudo tee /et ...