欢迎回来
登录你的知识库账户
忘记密码?
还没有账户?立即注册
创建账户
注册你的专属知识库
已有账户?去登录
找回密码
输入注册邮箱获取验证码
返回登录
请输入图片中的验证码以继续注册
加载中...
取消
新建收藏
手动添加你喜欢的内容
取消
编辑头像与昵称
上传新头像或修改你的显示昵称
支持 JPG/PNG,最大 2MB
取消

问题反馈

notebasewww.notebase.cn
控制台
内容库
动态
管理
账户
U
用户
--
在线
v0.8.7 · 知识库
笔记
KnowledgeBase
网络无边,知识有迹。
0笔记
0工具
30推荐

分类导航

按主题直达

编辑精选

站内用户贡献 · 真实笔记

最新收录

每日更新
继续浏览全部内容 →
>
笔记
0
加载中...
工具
0
此页用于记录用户反馈问题后的每一次改进
笔记用法

“写笔记”支持四种格式——Word 文档、Excel 表格、Markdown、纯文本,起稿或二次编辑时都能随时切换,同一篇笔记想用哪种形态来记,都由你说了算。

md、txt、csv、json 这类纯文本则原样载入,不做多余加工。拿一张现成的表倒进来、改几笔、再导出去,等于白用一台免费的格式转换器。

要带走就在右上角点“下载”,可导出 PDF、Word、Markdown、Excel、TXT 等格式;列表卡片“⋯”菜单里,也有同样的下载入口。

工具用法

在“工具”页点“+ 上传工具”即可发布:填好名称与链接,再用 Markdown 把使用方法写清楚——能解决什么问题、怎么装、怎么用,比堆介绍实在。

要分发安装包就一并上传压缩包(ZIP、RAR、7Z、TAR.GZ,最大 35MB),别人在详情页一键下载;只放链接不带附件也可以。

工具按大家的收藏热度排序,好用的自然会被顶上来。发布后可在详情页或卡片菜单里编辑、下架。

隐藏笔记

写笔记时勾上“隐藏”,这篇就只存在于你自己的账号里:不进列表、不进搜索、不上首页精选,也不会出现在任何公开的页面,链接发给别人同样打不开。

适合放密码、草稿、日记这类只给自己看的内容;想公开,去“发布”打开它,把“隐藏”的勾去掉再保存,之后编辑会默认保持原状态,不会悄悄变回公开。

数据安全

你的内容会同时保存在多个副本上,系统定期做备份与完整性校验,再配合异地容灾机制:就算某台机器出问题,数据也不会丢,可以长期放心存放;特别重要的资料,仍建议你另外再留一份备份。

技术

全站跑在容器化、模块化的现代架构上,更新、部署、回滚都很快,扩展性和稳定性都按长期运营的标准来设计(Built for reliability, designed to scale)。

理念

这个网站最早只是一个人的笔记仓库,后来慢慢长成现在的知识中枢。设计上很克制——没有广告、没有追踪、没有推荐算法,只是干干净净地存放一些东西;既然做好了,就公开出来,万一有人用得上呢。

原则

不做大而全,不做平台梦,保持简单、保持克制、保持好奇。所有内容都由用户贡献、由用户维护:不会突然冒出付费墙,不会在角落塞广告位,也不会把你的数据卖给第三方。

更多

产品会持续迭代,站内日志页记录着每一次改动,改了什么都有迹可循;想了解这个站是怎么一步步走到今天的,翻翻日志就能看到来龙去脉。

举报

如果在这里看到涉嫌违规的内容,点对应卡片右侧的“举报”按钮就能提交,我们会尽快核实处理;也谢谢你花一点时间,一起把这里维护干净。

趋势
// 点击导航加载发现
归档
// 归档为空
最近浏览
// 暂无浏览记录
发布
// 加载中...
用户发布
// 加载中...
用户管理
// 加载中...
访问统计
// 加载中...
内容审核
// 加载中...
个人信息
// 加载中...
返回首页

复制监控 — 高可用仪表盘

2026/7/7数据库

前言:为什么“有副本”不等于“高可用”

先说个真实场景。你搭好了Postgres流复制,primary和standby跑得挺好,团队都觉得“HA了,稳了”。然后某天primary挂了,你切到standby,结果发现数据落后了18个小时——因为副本三天前就断开了连接,但没人知道。

这不是段子,是我见过不止一次的翻车现场。Postgres的流复制本质上是一条WAL隧道:primary为每个standby启动一个walsender进程,standby那边跑walreceiver收WAL,再丢给startup process去replay。架构听着简单,但运维的痛点从来不是“怎么搭”,而是“副本死了你什么时候知道”。

HA仪表盘,就是一套把pg_stat_replication、pg_stat_wal_receiver和pg_replication_slots里的状态信号变成可视化和告警的系统。它的目标是让所有坏状态——副本断连、replay卡住、slot撑爆WAL、同步副本消失——在变成事故之前,变成一眼能看到的数字。


流复制的核心机制:四个LSN和三个lag

要理解监控什么,先得理解数据怎么流。

在primary上,每个已连接的standby对应一个walsender后端。Postgres从pg_wal/目录(如果WAL还在磁盘上)或者直接从WAL buffers(如果刚产生)读取WAL记录,然后通过复制连接流式发送。

在standby那边,walreceiver收到每条WAL记录后,先写到本地的pg_wal/(写到OS page cache,还没落盘),然后根据synchronous_commit配置决定是否fsync。之后startup process把这些记录replay到shared buffers——这一步才是“数据在standby上可见”的时刻。

这个流程里,有四个关键位置,对应pg_stat_replication里的四列:

  • sent_lsn:primary已经发送到socket的最后一个字节
  • write_lsn:standby已经write()到OS page cache的最后一个字节
  • flush_lsn:standby已经fsync落盘的最后一个字节
  • replay_lsn:standby已经replay到shared buffers的最后一个字节(数据真正可见)

Postgres文档明确规定了顺序:replay_lsn <= flush_lsn <= write_lsn <= sent_lsn <= pg_current_wal_lsn()。这四个值就是四个“水位标记”,它们之间的差距就是lag。

另外还有三个带_lag的列(write_lag、flush_lag、replay_lag),类型是interval,表示standby比primary慢了多少时间。这些时间是通过standby定期发送的feedback消息计算的。

核心查询:primary上的全景视图

SELECT
    application_name,
    client_addr,
    state,  -- streaming | catchup | startup | backup | stopping
    sync_state,  -- async | potential | sync | quorum
    pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn) AS sent_lag_bytes,
    pg_wal_lsn_diff(pg_current_wal_lsn(), write_lsn) AS write_lag_bytes,
    pg_wal_lsn_diff(pg_current_wal_lsn(), flush_lsn) AS flush_lag_bytes,
    pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS replay_lag_bytes,
    write_lag,
    flush_lag,
    replay_lag,
    EXTRACT(EPOCH FROM (now() - reply_time)) AS seconds_since_reply
FROM pg_stat_replication;

这个查询把每个standby的实时状态全扒出来了。注意seconds_since_reply——如果这个值持续增大,说明standby可能已经失联,只是TCP连接还没被操作系统关掉。

standby侧的视角

在standby上,pg_stat_wal_receiver告诉你它连的是哪个primary(conninfo、sender_host),当前状态(streaming/waiting/stopping),以及最后一次消息的收发时间。这个视图是判断standby是否真正在线的唯一证据——如果这个视图没有行,说明standby根本没连上primary或者连接断了没重连。

另外两个关键函数:

  • pg_last_wal_replay_lsn():最后一次replay的LSN
  • pg_last_xact_replay_timestamp():最后一次replay的事务提交时间

用now() - pg_last_xact_replay_timestamp()就能算出“standby上读到的数据比primary慢了多少秒”。这是业务最关心的指标——用户刚提交的数据,在standby上多久才能读到。

别忘了replication slots

pg_replication_slots是个容易被忽略但极其重要的视图。Slot的作用是pin住restart_lsn,防止primary回收standby还没消费的WAL段。

从Postgres 13开始,wal_status列有四个值:

  • reserved:正常,WAL安全
  • extended:WAL保留量超过正常范围,但还没丢
  • unreserved:WAL可能被回收,但还没丢
  • lost:已经超过max_slot_wal_keep_size,WAL被回收了,consumer必须从头做base backup

safe_wal_size告诉你当前slot还能扛多少字节的WAL不被回收。


生产环境常见的五种翻车场景

故障模式1:副本断连,但没人知道

这是最常见的。没有仪表盘的时候,发现方式只有一种:某天做failover,切到standby一看LSN,发现落后了几个小时甚至几天。

原因五花八门:

  • 防火墙或网络设备因为idle timeout把TCP连接掐了
  • walreceiver进程crash了
  • standby上的wal_receiver_timeout到期(网络抖动导致)
  • standby重启后primary_conninfo配错了

最坑的是,standby从pg_stat_replication消失后,primary的日志只会淡淡地打印一行terminating walsender process due to replication timeout,然后就没有然后了。

常见的错误做法:只检查副本数量

-- 这不够!如果两个副本掉了一个,你只知道“还有1个”,不知道哪个没了
SELECT count(*) AS replica_count FROM pg_stat_replication;

正确的做法:按名字告警

-- 每个standby必须设置application_name
SELECT
    expected.name,
    CASE
        WHEN r.application_name IS NULL THEN 'DOWN'
        WHEN r.state <> 'streaming' THEN r.state
        ELSE 'ok'
    END AS health
FROM
    (VALUES ('standby-1'), ('standby-2')) AS expected(name)
    LEFT JOIN pg_stat_replication r ON r.application_name = expected.name;

关键前提:每个standby必须在primary_conninfo里设置application_name。比如:

primary_conninfo='host=primary-ip port=5432 user=repl password=xxx application_name=standby-1'

没有这个,你没法区分哪个副本掉了。

故障模式2:副本断了但slot还在——pg_wal/爆炸

如果standby配置了primary_slot_name(用了物理复制槽),当standby断开后,slot的active变成false,但restart_lsn不会往前推进。primary会认为“这个slot还需要从restart_lsn开始的WAL”,于是所有从那个位置往后的WAL段都得保留。

结果是pg_wal/目录无限膨胀,直到磁盘写满,Postgres PANIC:

could not write to file ... No space left on device

GitLab 2017年的数据库事故就是这剧本——监控没覆盖到slot状态,等发现时已经晚了。

Postgres 13引入了max_slot_wal_keep_size作为硬限制,但光设这个不够,你还得监控wal_status:

-- 每个slot的状态必须上仪表盘
SELECT
    slot_name,
    slot_type,
    active,
    wal_status,
    pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained,
    pg_size_pretty(safe_wal_size) AS safe_left
FROM pg_replication_slots;

当wal_status变成extended或lost时,必须立刻告警——前者是警告,后者是灾难。

故障模式3:同步复制让所有写操作卡死

这是最隐蔽的坑。假设你配了:

synchronous_commit = on
synchronous_standby_names = 'FIRST 1 (standby-1)'

当standby-1断开后,primary上的每个COMMIT都会等待standby-1的ACK。客户端看到的是连接hang住,而不是报错。从应用层看,数据库“卡了”,但没有错误日志。

在pg_stat_activity里,这些等待的后端会显示:

  • wait_event_type = 'IPC'
  • wait_event = 'SyncRep'

如果你只关注sync_state列,不告警“同步副本数量低于阈值”,那等发现的时候,业务已经挂了半天了。

改进方案:

  1. 用quorum代替FIRST 1:ANY 2 (s1, s2, s3)允许任意一个副本挂掉,只要还有两个活着
  2. 硬性告警:当sync_state IN ('sync', 'quorum')的副本数低于所需阈值时立刻报警

故障模式4:replay_lag大但flush_lag接近零——replay冲突

这个场景特别容易误诊。你看到write_lsn紧贴着sent_lsn,但replay_lsn越拉越远。数据已经收到了、落盘了,但就是replay不进去。

原因通常是recovery conflict:

  • standby上有个长查询在跑,持有snapshot,导致vacuum产生的WAL无法apply
  • 某个表上有锁被hold住了

症状就是:用户刚在primary上commit了一条数据,切到standby上读不到。不是网络问题,是replay被block了。

在standby上查这个:

SELECT * FROM pg_stat_database_conflicts;

重点关注confl_snapshot、confl_lock、confl_bufferpin。

解决思路:

  • 开hot_standby_feedback = on:减少snapshot冲突,但代价是primary上会产生更多bloat(因为vacuum不敢清理standby还在用的tuple)
  • 调大max_standby_streaming_delay:让replay等一会再决定是干掉query还是继续等,但会增加lag
  • 架构层面:把长查询路由到专门的只读副本,别跟需要低延迟读的查询混在一起

故障模式5:仪表盘只在出事后才看

这是最“人”的问题。Metric配了,面板搭了,但没人日常看。等PagerDuty响了才打开Grafana——这时候MTTR已经包含了“人肉发现”的时间。

解决方案:

  1. 每个故障模式都要有对应的告警阈值,不能只靠人眼看
  2. 每个告警都要有runbook——收到告警后第一步做什么、第二步做什么
  3. 定期做混沌工程:手动断掉副本连接,看告警会不会响,响应对不对

总结:一个完整的HA仪表盘应该监控什么

维度 查询来源 关键指标 告警条件
副本在线状态 pg_stat_replication 每个application_name是否存在,state是否为streaming 任何已知副本消失或state异常
复制延迟 pg_stat_replication replay_lag_bytes, replay_lag (interval) 超过阈值(根据业务容忍度)
同步副本健康 pg_stat_replication sync_state in ('sync','quorum')的数量 低于所需同步副本数
Slot状态 pg_replication_slots wal_status, safe_wal_size wal_status = 'extended'或'lost'
Replay冲突 pg_stat_database_conflicts confl_snapshot等计数 持续增长或超过阈值
副本自身状态 pg_stat_wal_receiver 是否有行,last_msg_receipt_time 无行或长时间未收到消息

最后说一句:监控不是为了好看,是为了在坏事发生前给你留出反应时间。 如果你配了HA但没配这些监控,那你的HA可能只是自我安慰。

编写使用方法
Markdown 格式 · Ctrl+Enter 确定
新建笔记
预览
数据表格
点击单元格编辑 · Tab 移动
A1fx
Sheet1
BIH1H2≡🔗</>
隐私提醒

取消
编辑工具
取消