需熟悉的,PG数据库运行机制

需熟悉的,PG数据库运行机制

1.数据库时间线,lsn,wal日志:

简单来说,wal文件的命名是由时间线、lsn组成的,其中lsn由日志id和偏移量组成。时间线是记录数据库变更操作的标识,lsn是WAL日志唯一的、全局的标识。

LSN与数据恢复:

检查点(数据落盘的点)开始,回放WAL日志,如果数据页面的LSN小于WAL记录的LSN,则说明数据页面比较旧,需要进行回放,反之则不需要回放,就会跳过回放过程。

1.1、数据库时间线

记录数据库变更操作的标识,每个实例都有一个递增的数字表示时间线,作用是数据复制时确定数据的变更顺序和一致性,新主库会创建新的时间线保证数据一致性。

会改变时间线的情况:

  • 数据库恢复完成后,自动另起一条个时间线
  • 数据库重启

1.2、lsn

LSN即Log sequence number,日志序列号,这是WAL日志唯一的、全局的标识。

获取当前lsn号

postgres=# select pg_current_wal_lsn();
 pg_current_wal_lsn 
--------------------
 12/6000148
(1 row)

wal日志LSN编号规则:

12/6000148(高32位/低32位)

LSN 形式为 X/Y,其中 X 是 Log File Number,Y 是 Byte Offset(通常是从文件的开始位置算起的偏移量,以字节为单位)。

1.3、wal日志

WAL日志的命名说明如下:

一个段文件的名称由24个十六进制组成,分为三个部分,每个部分由8个十六进制字符组成。第一部分表示时间线,第二部分表示日志文件标号,第三部分表示日志文件的段标号。时间线由1开始,日志文件标号和日志文件的段标号由0开始。

postgres=# select pg_walfile_name('12/6000148');
     pg_walfile_name      
--------------------------
 000000390000001200000006
(1 row)

postgres=# 

### 偏移量计算
postgres=# select pg_walfile_NAME_OFFSET(pg_current_wal_lsn());
     pg_walfile_name_offset     
--------------------------------
 (000000390000001200000006,328)
(1 row)

postgres=# select pg_current_wal_lsn();
 pg_current_wal_lsn 
--------------------
 12/6000148
(1 row)

postgres=# select x'148'::int;
 int4 
------
  328
(1 row)

postgres=# 

wal命名规则

wal日志的logseg前6位始终是0,后两位是LSN低32位/16MB(2*24)
LSN在wal日志中的偏移量即LSN低32位中后24位对应的十进制值。即LSN的前两位。如上例中logseg最后两位是06,LSN低32前两位也是06。

组成部分十六进制值十进制值说明
文件前缀0000003957表示timeline;
日志段号0000001218表示logid;
文件内偏移000000066表示表示logseg

2.检查点、数据恢复

简单来说,如果要进行数据恢复,需要找到最近一次数据落盘的点(检查点),然后根据归档日志的信息重放sql操作,使其达到数据一致

  • 检查点是checkpoint进程刷脏的记录点,说明这个点之前的数据已经落盘,这个点之前的wal日志都不需要了,不需要关心两个检查点之间的wal日志。
  • 数据恢复:恢复数据库需要找到上一次落盘的点,根据上次落盘的点,去通过wal日志回放,wal日志是否完整是数据库自身保证的,只要事务提交了,那么wal日志一定会写入磁盘(也有不正常关机的情况)
1、检查点发生时,checkpoint进程会把共享缓冲区中的`脏块写入磁盘`,永久保存,以防主机断电等故障导致的数据丢失。此`频率由参数checkpoint_timeout`控制。
2、实例恢复:当实例发生崩溃后,再次启动需进行实例恢复,此时数据库将以`最近一次ckpt的位置`作为参考,开始`recovery`。
3、介质恢复:`物理备份时都会生成一个检查点`,用于判断将来恢复时的起点。开始备份的位置就是`将来recovery的位置`,在wal归档日志的帮助下使其同步。

3.复制槽:

  • 本质上是一个内存,主库保证不会删除备库(逻辑复制订阅端)没有应用的日志

  • 可以通过 pg_replication_slots动态视图的Active字段记录从库(订阅端)状态,可以在动态视图pg_stat_replication上查看sent_lsnreply_lsn,而且,replication slot 的状态信息是持久化保存的,即便从库断掉或主库重启,这些信息仍然不会丢掉或失效。

3.1、查看主备、库之间的复制延迟

核心思路就是:

  • 在主库上对比sent_lsnreplay_lsn之间的差值,使用函数pg_wal_lsn_diff(sent_lsn ,replay_lsn)
  • 在备库上去对比内置函数pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn()
postgres=# select * from pg_stat_replication;
-[ RECORD 1 ]----+------------------------------
pid              | 3512
usesysid         | 16388
usename          | repuser
application_name | postgresql110
client_addr      | 192.168.6.110
client_hostname  | 
client_port      | 7291
backend_start    | 2024-08-19 13:20:31.400284+08
backend_xmin     | 
state            | streaming
sent_lsn         | 12/6000148
write_lsn        | 12/6000148
flush_lsn        | 12/6000148
replay_lsn       | 12/6000148
write_lag        | 
flush_lag        | 
replay_lag       | 
sync_priority    | 0
sync_state       | async
reply_time       | 2024-08-19 17:19:28.043247+08

postgres=# 



#### 在备库上

SELECT
    pg_last_wal_receive_lsn() AS received_lsn,
    pg_last_wal_replay_lsn() AS replayed_lsn,
    pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn()) AS wal_delay_bytes
FROM
    pg_stat_wal_receiver;


postgres=# SELECT
postgres-#     pg_last_wal_receive_lsn() AS received_lsn,
postgres-#     pg_last_wal_replay_lsn() AS replayed_lsn,
postgres-#     pg_wal_lsn_diff(pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn()) AS wal_delay_bytes
postgres-# FROM
postgres-#     pg_stat_wal_receiver;
 received_lsn | replayed_lsn | wal_delay_bytes 
--------------+--------------+-----------------
 12/6000148   | 12/6000148   |               0
(1 row)

postgres=# 

###在主库上

SELECT
    client_addr,
    pg_current_wal_lsn() AS current_lsn,
    sent_lsn,
    replay_lsn,
    pg_wal_lsn_diff(sent_lsn, replay_lsn) AS wal_delay_bytes
FROM
    pg_stat_replication;

postgres=# SELECT
postgres-#     client_addr,
postgres-#     pg_current_wal_lsn() AS current_lsn,
postgres-#     sent_lsn,
postgres-#     replay_lsn,
postgres-#     pg_wal_lsn_diff(sent_lsn, replay_lsn) AS wal_delay_bytes
postgres-# FROM
postgres-#     pg_stat_replication;
-[ RECORD 1 ]---+--------------
client_addr     | 192.168.6.110
current_lsn     | 12/6000148
sent_lsn        | 12/6000148
replay_lsn      | 12/6000148
wal_delay_bytes | 0

postgres=# 

postgres=# 

pg_replication_slots 视图中的字段解释整理成表格格式。以下是一个示例表格:

字段描述示例值
slot_name复制槽的名称fd
plugin用于逻辑复制的插件名称logical1
slot_type复制槽的类型,physicallogicallogical
datoid数据库的 OID(对象标识符)5
database逻辑复制槽所关联的数据库名称postgres
temporary指示复制槽是否是临时的f
active指示复制槽是否处于活动状态f
active_pid当前与复制槽关联的进程 ID(PID)
xmin复制槽所关心的最小事务 ID(逻辑复制槽通常为空)
catalog_xmin系统目录中最小的事务 ID,逻辑复制槽需要保留这些事务 ID57755
restart_lsn复制槽重新启动时的 WAL 位置11/F6000180
confirmed_flush_lsn复制槽确认已被消费的 WAL 位置11/F60001B8
wal_status表示 WAL 的状态(通常为 reservedreserved
safe_wal_size与复制槽相关的安全 WAL 大小(逻辑复制槽通常为空)
two_phase指示是否支持两阶段提交(通常与物理复制相关)f
conflicting指示是否存在与复制槽相关的冲突f

3.2、查看wal的堆积情况

总结:使用pg_current_wal_insert_lsn()获取PostgreSQL 当前正在写入的 WAL 位置与restart_lsn复制槽开始复制的位置之间的差值。

引起这种情况的原因:

  • VACUUM风暴,通常是指在数据库中同时发生大量的自动或手动VACUUM操作,这些操作可能会产生大量WAL日志,从而导致I/O负载显著增加,影响数据库性能,同时也可能导致WAL无法被及时清理。
  • 大量的数据写入,大规模写入时出现的情况与VACUUM风暴相似。
  • wal_keep_size和max_wal_size参数设置过大都可能会导致实例保留大量WAL日志
  • 备库的wal堆积, 只读库上有长事务运行,hot_standby_feedback和max_standby_streaming_delay(hot_standby_feedback 控制是否在从库查询时发送反馈给主库,防止主库丢弃可能还需要的 WAL 数据。
    max_standby_streaming_delay 设定从库允许的最大 WAL 数据延迟时间,以便在处理长时间运行的查询时保持同步)设置不合理。

pg_stat_archiver 视图可以查看关于 WAL 日志归档的统计信息,包括归档进度和错误信息。

postgres=# SELECT * FROM pg_stat_archiver;
-[ RECORD 1 ]------+---------------------------------
archived_count     | 13
last_archived_wal  | 0000003800000011000000FE.partial
last_archived_time | 2024-08-19 11:03:11.469306+08
failed_count       | 0
last_failed_wal    | 
last_failed_time   | 
stats_reset        | 2024-08-15 09:54:55.715856+08

参数说明

参数说明实际值
archived_count已成功归档的 WAL 文件数量。13
last_archived_wal最后一次成功归档的 WAL 文件名称。0000003800000011000000FE.partial
last_archived_time最后一次成功归档的时间戳。2024-08-19 11:03:11.469306+08
failed_count归档失败的 WAL 文件数量。0
last_failed_wal最后一次归档失败的 WAL 文件名称(如果有)。(空)
last_failed_time最后一次归档失败的时间戳(如果有)。(空)
stats_reset统计信息最后重置的时间戳。2024-08-15 09:54:55.715856+08

这个表格展示了 pg_stat_archiver 视图中各个参数的说明和查询结果的实际值。

查询复制槽上的数据堆积量


postgres=# SELECT slot_name, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_insert_lsn(), restart_lsn)) AS delay_size
FROM pg_replication_slots;
   slot_name   | delay_size 
---------------+------------
 fdmq2         | 256 MB
 fdmq1         | 256 MB
 logicreplia   | 256 MB
 postgresql110 | 0 bytes
(4 rows)

postgres=# 

参数说明:

参数描述
slot_name复制槽的名称。
pg_current_wal_insert_lsn()当前 WAL 插入的位置(Log Sequence Number, LSN)。表示 PostgreSQL 当前正在写入的 WAL 位置。
restart_lsn复制槽的重启位置。对于逻辑复制槽,表示从这个位置开始重新进行数据流式复制。
pg_wal_lsn_diff()计算两个 LSN 之间的差异(即 WAL 的堆积量)。在本查询中,计算 pg_current_wal_insert_lsn()restart_lsn 之间的差异。
pg_size_pretty()将字节数转换为更易读的格式(例如,KB、MB 或 GB)。用于将计算得到的 WAL 差异转换为可读格式。
delay_sizeWAL 延迟大小,表示自复制槽的重启位置到当前 WAL 插入位置的 WAL 数据量。

在 PostgreSQL 中,pg_current_wal_lsn()pg_current_wal_insert_lsn() 这两个函数有以下区别:

  • pg_current_wal_lsn(): 返回当前的 WAL(Write-Ahead Log)插入位置。它反映了数据库当前正在写入 WAL 的位置。
  • pg_current_wal_insert_lsn(): 这个函数用于逻辑复制槽,返回当前的 WAL 插入位置与复制槽的起始位置之间的差异。它用于监视逻辑复制槽的进度。

总的来说,pg_current_wal_lsn() 是用于一般情况的函数,而 pg_current_wal_insert_lsn() 主要用于逻辑复制的监控。

4、PG生产环境,故障转移

总结:这里主要介绍了Patroni集群环境参数配置,和主备切换、故障转移保证数据不丢失的原理。

生产环境,参数配置需要注意的地方

  • 一定注意shared_buffersmax_connections这两个参数,数据库最大连接一般默认100到200 。

  • 一个连接就是一个单独的进程,这些进程都会去数据库里跑SQL,都会占用共享内存,64核心的cpu物理机,配置的数据库连接数过大会影响性能。

  • Patroni集群中的全局参数,涉及到数据一致性和复制要求,必须在所有节点上保持一致(比如:wal_level 和 max_wal_senders 直接影响到 WAL 日志的生成和传输)。

  • Patroni集群中的本地参数,则可以根据各个节点的具体需求进行调整(比如:通过为从节点配置更大的 work_mem 来优化查询性能)

Patroni保证高可用和故障转移

高可用性是指系统能够在较长时间内持续正常运行,并能在出现故障时迅速恢复。故障转移是指在主节点发生故障时,自动将服务切换到一个健康的从节点,以保证服务的连续性

高可用直观表现为:系统故障时间越短,故障次数间隔时间越长,高可用越好。
可用性 = 工作时间 /(工作时间 + 停机时 间)

  • Patroni通过流复制机制实现异步或同步的高可用切换策略。它允许设置“maximum_lag_on_failover”,确保在主节点故障时,备节点的数据滞后不会超出预定阈值。
  • Patroni可以检测节点状态,把节点状态信息存入到zookeeper中,当Leader节点不可用时,Patroni会自动触发选举过程,选择一个Replica节点作为新的Leader

Patroni的主备切换和故障转移,不丢失数据的原理如下:

  • 1、如果是在Patroni的主、备库流复制之间发生主备切换(或者故障转移)(因为备库这时候没有复制槽),是通过pg_rewind工具来修正新的主备节点的wal日志同步,是使用参数 use_pg_rewind: true来控制的。
  • 2、如果是在Patroni的主、备库流复制之间某个备库节点宕机重启,是通过主库的复制槽来确保数据同步。
  • 3、如果在Patroni集群的环境上,有个逻辑复制到MQ的业务,这时发生主备切换是通过永久性复制槽来保证高可用,使用patronictl -c postgres0.yml edit-config命令去编辑。
  • 4、永久性复制槽在非主库节点上是不活跃的。

5、逻辑复制槽的推进机制

总结:逻辑复制槽推进,通过restart_lsncatalog_xmin,以保护xlog和系统表。
物理复制restart lsn的推进比较简单,当备机flush某个lsn以后,告诉主机,主机收到后,立即就会推进restart lsn

5.1、推进逻辑复制槽进度的作用,控制的参数

推进逻辑复制槽的作用

  • 一个是保护系统表避免被vacuum,一个是保护xlog,防止需要解码的xlog被回收。
  • 如果系统表不往前推进,则系统表就会发生膨胀。如果xlog不往前推进,xlog就会堆积在磁盘。
  • 所以,我们根据当前逻辑复制的进度,推进逻辑复制槽。

控制的参数

  • 对于xlog,与物理复制槽相同,通过restart_lsn控制。
  • 对于系统表,通过catalog_xmin控制。

5.2、restart_lsn的推进

物理、逻辑复制restart_lsn的推进过程

  • 物理复制restart lsn的推进比较简单,当备机flush某个lsn以后,告诉主机,主机收到后,立即就会推进restart lsn
  • 而逻辑复制,则相对比较复杂。
    • 我们用confirmed_flush表示当前订阅端逻辑复制的进度。但是,不是说confirmed_flush之前的xlog就可以回收。
    • 因为逻辑解码需要历史快照,而历史快照的构建本质就是根据running_xacts以及后续的commit等xlog来完成的
    • 所以为了能够解析后面的xlog,restart lsn还要对后续解码需要用的快照xlog进行保护。
    • 那么此时就得看confirmed_flush更新时,需要用到的最老的快照lsn是多少了。
    • 所以我们用两个lsn实现restart_lsn的推进candidate_restart_valid就表示当前解码的进度lsn,而candidate_restart_lsn表示解码到candidate_restart_valid的时候,需要用的最老的快照的lsn。
    • confirmed_flush超过candidate_restart_valid时,就可以推进restart_lsn到candidate_restart_lsn了,可以进行更新了。

5.3、catalog_xmin的推进

catalog_xmin的推进

  • 与restart lsn的推进是类似的。
  • 在序列化快照的时候,使用candidate_xmin_lsn表示当前解码的进度,使用candidate_catalog_xmin表示当前解码需要用的最老xmin
  • confirmed_flush超过candidate_xmin_lsn的时候,就可以更新复制槽的catalog_xmincandidate_catalog_xmin

5.4、参数说明:

参数类型解释
restart_lsn逻辑复制控制xlog回收,保护需要解码的xlog和历史快照。
catalog_xmin逻辑复制控制系统表回收,保护当前解码进度所需的系统快照。
confirmed_flush逻辑复制表示当前订阅端逻辑复制的进度。
candidate_restart_valid逻辑复制当前解码进度的lsn。
candidate_restart_lsn逻辑复制解码到candidate_restart_valid所需的最老快照lsn。
candidate_xmin_lsn逻辑复制当前解码进度的xmin lsn。
candidate_catalog_xmin逻辑复制解码所需的最老xmin。

这些参数用于确保复制过程中的数据完整性和性能。

这一部分的源码请参考,其他文章

参考文章:
【1】https://blog.csdn.net/forrest_hu/article/details/134338007

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值