Skip to main content
Septvean's Documents
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

PostgreSQL 运维

PostgreSQL 运维的核心不是“数据库能启动”,而是做到四件事:

  1. 故障能够及时发现。
  2. 性能问题能够快速定位。
  3. 数据能够按目标时间恢复。
  4. 配置、升级和维护操作可以回滚。

对于你的 PostgreSQL 18 + TimescaleDB 股票数据环境,还要额外关注数据新鲜度、Hypertable 分块和后台策略任务。


一、运维体系

领域 核心目标 主要对象
可用性 数据库持续提供服务 进程、端口、连接
性能 SQL 延迟稳定 慢 SQL、锁、I/O、内存
容量 防止磁盘耗尽 数据、索引、WAL、日志
数据维护 控制膨胀、保持统计准确 VACUUM、ANALYZE
高可用 主库故障后可切换 流复制、复制延迟
数据保护 可恢复到指定时间 备份、WAL、PITR
安全 限制访问和高危权限 角色、SSL、pg_hba.conf
变更管理 升级和配置变更可控 配置、扩展、版本

建议建立三层检查:

  • 操作系统层:CPU、内存、磁盘、I/O、网络、OOM。
  • PostgreSQL 层:连接、锁、事务、WAL、Vacuum、复制。
  • 业务层:股票数据日期、数量、重复数据、任务状态。

二、数据库存活检查

1. 检查服务器是否接受连接

pg_isready \
  -h 127.0.0.1 \
  -p 5432 \
  -d ashare \
  -t 3

pg_isready 只能证明服务器有响应,最好再执行真实 SQL:

psql \
  -h 127.0.0.1 \
  -U monitor_user \
  -d ashare \
  -Atqc "SELECT 1"

2. 查看基本状态

SELECT
    now() AS checked_at,
    current_setting('server_version') AS server_version,
    pg_postmaster_start_time() AS started_at,
    now() - pg_postmaster_start_time() AS uptime,
    pg_is_in_recovery() AS is_standby;

其中:

  • is_standby = false:通常是主库。
  • is_standby = true:数据库处于恢复或备库状态。

三、连接与会话运维

1. 查看连接使用率

WITH conn AS (
    SELECT
        count(*) AS total,
        count(*) FILTER (WHERE state = 'active') AS active,
        count(*) FILTER (WHERE state = 'idle') AS idle,
        count(*) FILTER (
            WHERE state LIKE 'idle in transaction%'
        ) AS idle_in_transaction
    FROM pg_stat_activity
    WHERE backend_type = 'client backend'
),
cfg AS (
    SELECT current_setting('max_connections')::integer AS max_connections
)
SELECT
    conn.*,
    cfg.max_connections,
    round(100.0 * conn.total / cfg.max_connections, 2) AS usage_pct
FROM conn
CROSS JOIN cfg;

初始告警线可以设置为:

指标 参考告警值
连接使用率 70% 警告,85% 严重
idle in transaction 超过 1 分钟
普通 API SQL 超过 30 秒
锁等待 超过 10~30 秒

这些只是起始值,应根据实际基线和 SLA 调整。

不要一遇到连接不足就提高 max_connections。大量连接会增加进程、内存和调度开销,应该先检查:

  • 应用是否泄漏连接。
  • SQLAlchemy 连接池是否合理。
  • 是否有大量空闲连接。
  • 是否需要 PgBouncer。
  • API、ETL、管理任务是否使用独立账号和连接池。

建议应用设置明确的 application_name,这样能在 pg_stat_activity 中快速定位 FastAPI、行情采集和后台任务。

2. 查看长时间运行的 SQL

SELECT
    pid,
    datname,
    usename,
    application_name,
    client_addr,
    state,
    wait_event_type,
    wait_event,
    clock_timestamp() - query_start AS query_duration,
    clock_timestamp() - xact_start AS transaction_duration,
    left(query, 300) AS query
FROM pg_stat_activity
WHERE state = 'active'
  AND pid <> pg_backend_pid()
  AND query_start < clock_timestamp() - interval '30 seconds'
ORDER BY query_start;

state='active' 不代表 SQL 一直在使用 CPU。如果同时存在 wait_event,可能正在等待:

  • Lock:等待表锁、行锁。
  • IO:等待磁盘。
  • Client:等待客户端发送或接收数据。
  • LWLock:等待 PostgreSQL 内部共享结构。
  • IPC:等待其他后台进程。

PostgreSQL 18 可通过 pg_stat_activitypg_stat_iopg_stat_walpg_stat_checkpointer 等视图观察运行状态。PostgreSQL 18 统计监控文档

3. 查找空闲事务

SELECT
    pid,
    datname,
    usename,
    application_name,
    client_addr,
    state,
    clock_timestamp() - xact_start AS transaction_age,
    clock_timestamp() - state_change AS idle_age,
    left(query, 300) AS last_query
FROM pg_stat_activity
WHERE state LIKE 'idle in transaction%'
ORDER BY xact_start;

空闲事务危害很大:

  • 长时间保留旧版本数据。
  • 阻碍 VACUUM 清理。
  • 导致表和索引膨胀。
  • 长时间持有锁。
  • 可能拖慢备库清理。

可以针对 API 用户设置:

ALTER ROLE ashare_api IN DATABASE ashare
SET idle_in_transaction_session_timeout = '60s';

ALTER ROLE ashare_api IN DATABASE ashare
SET statement_timeout = '30s';

ALTER ROLE ashare_api IN DATABASE ashare
SET lock_timeout = '5s';

ETL 任务不应直接复用 API 的短超时配置,应使用独立角色和更合适的超时。


四、锁与阻塞排查

1. 查询阻塞关系

SELECT
    blocked.pid AS blocked_pid,
    blocked.usename AS blocked_user,
    blocked.application_name AS blocked_app,
    clock_timestamp() - blocked.query_start AS blocked_duration,
    left(blocked.query, 200) AS blocked_query,

    blocker.pid AS blocker_pid,
    blocker.usename AS blocker_user,
    blocker.application_name AS blocker_app,
    clock_timestamp() - blocker.xact_start AS blocker_xact_duration,
    left(blocker.query, 200) AS blocker_query
FROM pg_stat_activity AS blocked
JOIN LATERAL unnest(pg_blocking_pids(blocked.pid))
    AS p(blocker_pid) ON true
JOIN pg_stat_activity AS blocker
    ON blocker.pid = p.blocker_pid
ORDER BY blocked.query_start;

2. 处理阻塞

优先取消当前 SQL:

SELECT pg_cancel_backend(12345);

如果连接本身必须断开:

SELECT pg_terminate_backend(12345);

处理顺序应当是:

  1. 确认阻塞者身份和业务。
  2. 判断事务能否安全回滚。
  3. 优先通知应用主动提交或回滚。
  4. 使用 pg_cancel_backend()
  5. 最后才使用 pg_terminate_backend()

不要自动终止所有阻塞者,因为阻塞者可能正在执行关键资金、订单或批量导入事务。锁信息主要来自 pg_locks,但官方也建议结合 pg_stat_activitypg_blocking_pids() 分析。锁监控文档


五、慢 SQL 运维

1. 启用 pg_stat_statements

你的配置已经包含 TimescaleDB,因此要保留原值:

shared_preload_libraries = 'timescaledb,pg_stat_statements'
compute_query_id = on
pg_stat_statements.track = top

track_io_timing = on
track_wal_io_timing = on

修改 shared_preload_libraries 后需要重启 PostgreSQL。

在业务数据库中安装扩展:

CREATE EXTENSION IF NOT EXISTS pg_stat_statements;

查看累计资源消耗最大的 SQL:

SELECT
    queryid,
    calls,
    round(total_exec_time::numeric, 2) AS total_ms,
    round(mean_exec_time::numeric, 2) AS mean_ms,
    round(max_exec_time::numeric, 2) AS max_ms,
    rows,
    shared_blks_read,
    shared_blks_hit,
    temp_blks_written,
    wal_bytes,
    left(query, 300) AS query
FROM pg_stat_statements
WHERE dbid = (
    SELECT oid
    FROM pg_database
    WHERE datname = current_database()
)
ORDER BY total_exec_time DESC
LIMIT 20;

分析重点:

  • total_exec_time:累计占用最多的 SQL。
  • mean_exec_time:单次平均最慢。
  • calls:高频小 SQL。
  • temp_blks_written:排序或哈希发生磁盘溢出。
  • shared_blks_read:读取压力。
  • wal_bytes:写入产生的 WAL 数量。

pg_stat_statements 需要通过 shared_preload_libraries 加载,并依赖查询标识符。pg_stat_statements 文档

2. 推荐日志基线

logging_collector = on

log_min_duration_statement = '500ms'
log_lock_waits = on
deadlock_timeout = '1s'
log_temp_files = '64MB'
log_autovacuum_min_duration = '1s'

log_line_prefix = '%m [%p] %q%u@%d/%a '

不建议在生产库长期设置:

log_statement = 'all'

它可能:

  • 产生海量日志。
  • 记录密码、手机号、策略参数等敏感值。
  • 增加磁盘和 I/O 压力。

log_min_duration_statement 会记录超过指定耗时的完整 SQL,是定位慢查询的基础手段。PostgreSQL 日志配置


六、磁盘与容量管理

1. 查看各数据库大小

SELECT
    datname,
    pg_size_pretty(pg_database_size(datname)) AS database_size
FROM pg_database
WHERE datallowconn
ORDER BY pg_database_size(datname) DESC;

2. 查看最大的表和索引

SELECT
    schemaname,
    relname,
    pg_size_pretty(pg_relation_size(relid)) AS table_size,
    pg_size_pretty(pg_indexes_size(relid)) AS index_size,
    pg_size_pretty(pg_total_relation_size(relid)) AS total_size
FROM pg_stat_user_tables
ORDER BY pg_total_relation_size(relid) DESC
LIMIT 20;

这只能看到当前大小。要判断增长速度,必须定期采集并保存历史数据,例如每天记录:

  • 数据库总大小。
  • 最大表大小。
  • WAL 产生量。
  • 临时文件量。
  • 每日新增行情行数。

SQL 大小函数看不到文件系统剩余空间,还必须在操作系统层监控:

  • 数据目录所在磁盘。
  • pg_wal 所在磁盘。
  • 表空间磁盘。
  • 日志磁盘。
  • 备份目录。
  • inode 数量。

3. 磁盘即将满时

处理顺序:

  1. 确认是数据、WAL、日志、临时文件还是备份增长。
  2. 检查 WAL 归档是否失败。
  3. 检查复制槽是否长期不活跃。
  4. 检查日志轮转。
  5. 扩容或安全转移非数据库文件。
  6. 确认空间充足后处理根因。

绝对不要手工删除:

PGDATA/base
PGDATA/global
PGDATA/pg_xact
PGDATA/pg_wal

尤其不能通过删除 pg_wal 来解决磁盘告警,这可能直接让实例无法恢复。


七、VACUUM、ANALYZE 与膨胀

PostgreSQL 的 UPDATEDELETE 不会立即物理删除旧行,而是产生死元组,依靠 VACUUM 回收空间。

1. 查看死元组

SELECT
    schemaname,
    relname,
    n_live_tup,
    n_dead_tup,
    round(
        100.0 * n_dead_tup /
        greatest(n_live_tup + n_dead_tup, 1),
        2
    ) AS dead_pct,
    last_autovacuum,
    last_autoanalyze,
    autovacuum_count,
    autoanalyze_count
FROM pg_stat_user_tables
WHERE n_dead_tup > 1000
ORDER BY n_dead_tup DESC
LIMIT 30;

注意:n_live_tupn_dead_tup 是估算值,不能仅凭某个百分比判定表膨胀。

2. 手动维护

大批量导入、更新或删除后,可以在低负载时间执行:

VACUUM (ANALYZE, VERBOSE) market.daily_quote;

只更新统计信息:

ANALYZE market.daily_quote;

普通 VACUUM

  • 清理死元组。
  • 将空间标记为可重复使用。
  • 通常不会把空间归还操作系统。
  • 一般不阻塞正常增删改查。

VACUUM FULL

  • 重写整张表。
  • 需要额外临时磁盘空间。
  • 获取 ACCESS EXCLUSIVE 锁。
  • 会阻塞表的所有正常访问。

所以生产运维应优先调好 autovacuum,尽量避免依赖 VACUUM FULLPostgreSQL 日常 Vacuum 文档

3. 监控事务 ID 年龄

SELECT
    datname,
    age(datfrozenxid) AS xid_age,
    current_setting('autovacuum_freeze_max_age')::bigint
        AS autovacuum_trigger_age,
    round(
        100.0 * age(datfrozenxid) /
        current_setting('autovacuum_freeze_max_age')::numeric,
        2
    ) AS trigger_usage_pct
FROM pg_database
WHERE datallowconn
ORDER BY xid_age DESC;

这个百分比表示接近“强制防回卷 autovacuum”的程度,并不是直接表示距离 20 亿事务回卷还有多少。

如果持续接近阈值,应检查:

  • autovacuum 是否被长事务阻挡。
  • 是否有大量写入。
  • 表是否过大。
  • autovacuum worker 是否不足。
  • 是否存在 idle in transaction
  • 磁盘 I/O 是否已经饱和。

不要随意取消名称带有:

(to prevent wraparound)

的 autovacuum 任务。

4. 索引重建

索引确实膨胀或损坏时,生产库优先考虑:

REINDEX INDEX CONCURRENTLY market.idx_daily_quote_code_date;

并发重建允许正常读写,但耗时更长、资源开销更大。普通 REINDEX 会阻塞写入。REINDEX 官方说明

不能仅因为 idx_scan = 0 就删除索引,因为统计可能刚刚重置,也可能该索引只用于月末或故障查询。


八、PostgreSQL 18 I/O、WAL 与检查点

1. 查看 I/O

SELECT
    backend_type,
    object,
    context,
    reads,
    pg_size_pretty(read_bytes::bigint) AS read_size,
    round(read_time::numeric, 2) AS read_ms,
    writes,
    pg_size_pretty(write_bytes::bigint) AS write_size,
    round(write_time::numeric, 2) AS write_ms,
    fsyncs,
    round(fsync_time::numeric, 2) AS fsync_ms
FROM pg_stat_io
ORDER BY coalesce(read_bytes, 0) + coalesce(write_bytes, 0) DESC;

PostgreSQL 18 的 pg_stat_io 可以区分:

  • 普通 relation I/O。
  • 临时 relation I/O。
  • WAL I/O。
  • bulkreadbulkwritevacuum 等场景。

其中耗时列只有启用 track_io_timingtrack_wal_io_timing 后才有意义。

2. 查看 WAL 产生量

SELECT
    wal_records,
    wal_fpi,
    pg_size_pretty(wal_bytes::bigint) AS generated_wal,
    wal_buffers_full,
    stats_reset
FROM pg_stat_wal;

需要观察“单位时间增量”,不能只看累计值。

WAL 突然增长通常与以下操作有关:

  • 大批量 INSERTUPDATEDELETE
  • 创建或重建索引。
  • 大量全页写。
  • VACUUM FULL。
  • 批量回填历史行情。
  • 逻辑复制。

3. 查看检查点

SELECT
    num_timed,
    num_requested,
    num_done,
    write_time,
    sync_time,
    buffers_written,
    stats_reset
FROM pg_stat_checkpointer;

如果单位时间内 num_requested 远高于 num_timed,可能意味着:

  • max_wal_size 太小。
  • 写入突增。
  • 有程序频繁执行 CHECKPOINT
  • 备份或其他操作触发检查点。

也要结合磁盘延迟和 WAL 速率判断,不能只凭累计计数修改参数。


九、WAL 归档与备份监控

1. 查看归档状态

SELECT
    archived_count,
    failed_count,
    last_archived_wal,
    last_archived_time,
    last_failed_wal,
    last_failed_time,
    stats_reset
FROM pg_stat_archiver;

重点不是 failed_count 是否为零,而是它是否继续增长,以及最新 WAL 是否持续成功归档。

需要告警:

  • failed_count 增加。
  • 长时间没有成功归档,但数据库持续产生 WAL。
  • pg_wal 目录快速增长。
  • 备份超出预期完成时间。

2. 备份运维标准

每次备份至少记录:

  • 开始时间、结束时间。
  • PostgreSQL 和 TimescaleDB 版本。
  • 数据库或实例名称。
  • 备份类型。
  • 文件大小。
  • 命令退出码。
  • 校验结果。
  • 备份存储位置。
  • 保留期限。

物理备份可以执行:

pg_verifybackup /srv/pgbackup/base/2026-09-14

但只有成功完成独立恢复、业务查询和 PITR 演练,才能真正证明备份可用。

流复制备库不是备份:生产库误删数据后,误操作同样会复制到备库。


十、流复制运维

1. 主库查看复制状态

SELECT
    application_name,
    client_addr,
    state,
    sync_state,
    write_lag,
    flush_lag,
    replay_lag,
    CASE
        WHEN replay_lsn IS NULL THEN NULL
        ELSE pg_size_pretty(
            pg_wal_lsn_diff(
                pg_current_wal_lsn(),
                replay_lsn
            )::bigint
        )
    END AS replay_lag_bytes
FROM pg_stat_replication
ORDER BY application_name;

需要同时观察:

  • state 是否为 streaming
  • 字节延迟。
  • 时间延迟。
  • 备库能否实际查询。
  • 备库磁盘是否充足。

主库空闲时,pg_last_xact_replay_timestamp() 计算出的时间差可能看起来很大,因此时间延迟不能单独作为判断标准。

2. 检查复制槽

SELECT
    slot_name,
    slot_type,
    active,
    active_pid,
    wal_status,
    restart_lsn,
    confirmed_flush_lsn,
    CASE
        WHEN restart_lsn IS NULL THEN NULL
        ELSE pg_size_pretty(
            pg_wal_lsn_diff(
                pg_current_wal_lsn(),
                restart_lsn
            )::bigint
        )
    END AS retained_wal
FROM pg_replication_slots
ORDER BY slot_name;

长期不活跃的复制槽可能让主库无限保留 WAL,最终填满磁盘。可通过 max_slot_wal_keep_size 限制风险,但不要看到槽不活跃就直接删除,应先确认对应备库或订阅是否已经废弃。PostgreSQL 流复制说明


十一、TimescaleDB 专项运维

1. 检查版本和加载状态

SELECT
    extname,
    extversion
FROM pg_extension
WHERE extname = 'timescaledb';

SELECT
    name,
    default_version,
    installed_version
FROM pg_available_extensions
WHERE name = 'timescaledb';

SHOW shared_preload_libraries;

2. 查看 Hypertable

SELECT
    hypertable_schema,
    hypertable_name,
    num_dimensions,
    num_chunks,
    compression_enabled
FROM timescaledb_information.hypertables
ORDER BY hypertable_schema, hypertable_name;

3. 查看每个 Hypertable 的总大小

SELECT
    hypertable_schema,
    hypertable_name,
    pg_size_pretty(bytes) AS total_size
FROM (
    SELECT
        hypertable_schema,
        hypertable_name,
        hypertable_size(
            format(
                '%I.%I',
                hypertable_schema,
                hypertable_name
            )::regclass
        ) AS bytes
    FROM timescaledb_information.hypertables
) AS s
ORDER BY bytes DESC;

4. 查看 Chunk 数量和压缩状态

SELECT
    hypertable_schema,
    hypertable_name,
    count(*) AS chunk_count,
    count(*) FILTER (WHERE is_compressed) AS compressed_chunks,
    count(*) FILTER (WHERE NOT is_compressed) AS uncompressed_chunks
FROM timescaledb_information.chunks
GROUP BY hypertable_schema, hypertable_name
ORDER BY chunk_count DESC;

需要警惕:

  • Chunk 数量异常增长。
  • Chunk 太小,导致规划和元数据开销变大。
  • Chunk 太大,导致压缩、Vacuum 和索引维护耗时过长。
  • 压缩策略应执行但大量历史 Chunk 没有压缩。

5. 检查 TimescaleDB 后台任务

SELECT
    j.job_id,
    j.proc_name,
    j.hypertable_schema,
    j.hypertable_name,
    j.scheduled,
    j.schedule_interval,
    s.job_status,
    s.last_run_status,
    s.last_run_duration,
    s.last_successful_finish,
    s.next_start,
    s.total_runs,
    s.total_successes,
    s.total_failures
FROM timescaledb_information.jobs AS j
LEFT JOIN timescaledb_information.job_stats AS s
    USING (job_id)
ORDER BY
    s.total_failures DESC NULLS LAST,
    j.job_id;

查看错误详情:

SELECT *
FROM timescaledb_information.job_errors
ORDER BY finish_time DESC
LIMIT 20;

重点检查:

  • scheduled = false:任务是否被人为暂停。
  • last_run_status = 'Failed'
  • total_failures 是否持续增加。
  • last_run_duration 是否已经接近或超过调度间隔。
  • 连续聚合、压缩、保留策略是否按预期运行。

相关字段可参考 TimescaleDB job_stats


十二、A 股数据业务巡检

数据库正常不代表业务数据正常。行情库还需要业务级检查。

1. 查看最近交易日数据量

SELECT
    trade_date,
    count(*) AS row_count,
    count(DISTINCT code) AS stock_count
FROM market.daily_quote
WHERE trade_date >= current_date - 10
GROUP BY trade_date
ORDER BY trade_date DESC;

应该和交易日历比较,不能简单要求每天都有数据,因为周末和节假日不开市。

2. 检查重复行情

SELECT
    code,
    trade_date,
    count(*) AS duplicate_count
FROM market.daily_quote
GROUP BY code, trade_date
HAVING count(*) > 1
ORDER BY duplicate_count DESC
LIMIT 100;

建议存在唯一约束:

UNIQUE (code, trade_date)

3. 建议建立采集批次表

例如:

batch_id
data_source
trade_date
started_at
finished_at
expected_rows
actual_rows
success_rows
failed_rows
status
error_message
checksum

这样可以区分:

  • 数据源没有数据。
  • 爬虫失败。
  • 数据下载成功但入库失败。
  • 入库成功但数量不完整。
  • 重复执行导致数据覆盖。

大规模历史行情回填完成后,应执行:

ANALYZE market.daily_quote;

十三、配置变更管理

1. 查找需要重启才能生效的配置

SELECT
    name,
    setting,
    unit,
    context,
    source,
    sourcefile,
    sourceline,
    pending_restart
FROM pg_settings
WHERE pending_restart
ORDER BY name;

2. 检查配置文件错误

SELECT
    sourcefile,
    sourceline,
    name,
    setting,
    applied,
    error
FROM pg_file_settings
WHERE error IS NOT NULL
   OR NOT applied
ORDER BY sourcefile, sourceline;

检查 HBA:

SELECT *
FROM pg_hba_file_rules
WHERE error IS NOT NULL;

确认无误后重载:

SELECT pg_reload_conf();

部分参数只能重启生效,pending_restart=true 可以帮助识别。postgresql.auto.conf 中的 ALTER SYSTEM 配置会覆盖 postgresql.conf,排查配置“不生效”时必须一起检查。PostgreSQL 参数管理

3. 服务启停

pg_ctl status -D /path/to/data

pg_ctl stop \
  -D /path/to/data \
  -m fast

pg_ctl restart \
  -D /path/to/data \
  -m fast

生产环境通常由 systemd、容器编排或对应安装工具管理服务,避免同时使用多个服务管理方式。

关闭模式:

模式 行为
smart 等待所有客户端主动断开
fast 断开客户端并回滚事务,推荐日常使用
immediate 立即停止,下次启动进行崩溃恢复

除紧急情况外不要使用 immediate,更不要直接 kill -9 PostgreSQL 主进程。pg_ctl 官方文档


十四、安全运维

1. 创建专用监控角色

CREATE ROLE monitor_user LOGIN;

GRANT pg_monitor TO monitor_user;

维护角色可以按需授予:

GRANT pg_maintain TO maintenance_user;

pg_monitor 用于读取监控视图,pg_maintain 可执行 VACUUM、ANALYZE、REINDEX 等维护操作。它们比直接授予超级用户更安全。PostgreSQL 预定义角色

2. 审计高权限角色

SELECT
    rolname,
    rolsuper,
    rolcreaterole,
    rolcreatedb,
    rolreplication,
    rolbypassrls,
    rolcanlogin,
    rolvaliduntil
FROM pg_roles
ORDER BY
    rolsuper DESC,
    rolcreaterole DESC,
    rolname;

基本原则:

  • 应用程序不能使用超级用户。
  • 对象所有者最好使用 NOLOGIN 角色。
  • API、行情采集、只读分析、备份、监控使用不同角色。
  • 限制 CONNECT、Schema USAGE 和表权限。
  • 定期检查长期不使用的登录角色。
  • 远程连接优先使用 hostssl
  • 避免 trust 和宽泛的 0.0.0.0/0
  • 使用 SCRAM-SHA-256。

PostgreSQL 18 已将 MD5 密码支持标记为弃用;pg_hba.conf 按从上到下寻找第一条匹配规则,不会在认证失败后继续匹配下一条。pg_hba.conf 文档


十五、常见故障处理流程

数据库无法连接

依次检查:

  1. pg_isready
  2. PostgreSQL 服务状态。
  3. 数据库日志。
  4. 磁盘和 inode。
  5. 是否发生 OOM。
  6. max_connections 是否耗尽。
  7. 端口和 listen_addresses
  8. pg_hba.conf
  9. TLS 证书是否过期。
  10. 数据目录权限。

数据库突然变慢

依次检查:

  1. 活跃会话和长 SQL。
  2. 锁阻塞。
  3. 长事务和空闲事务。
  4. pg_stat_statements
  5. 临时文件增长。
  6. pg_stat_io
  7. CPU、内存和磁盘延迟。
  8. autovacuum 是否被阻塞。
  9. WAL、检查点和归档。
  10. TimescaleDB 后台任务。
  11. 最近是否发布代码或修改执行计划相关配置。

磁盘暴涨

重点检查:

  • 归档失败造成 pg_wal 累积。
  • 不活跃复制槽保留 WAL。
  • 日志没有轮转。
  • 大事务产生大量临时文件。
  • 批量更新造成表、索引膨胀。
  • 备份存放在数据盘且未清理。
  • TimescaleDB Chunk 或连续聚合快速增长。

误删数据

  1. 立即停止相关写入任务。
  2. 记录误操作时间、时区、SQL 和相关用户。
  3. 保留当前 WAL。
  4. 在隔离实例执行 PITR。
  5. 验证恢复点。
  6. 导出误删对象。
  7. 审核后导回生产库。

不要直接在生产数据目录上反复尝试恢复。


十六、推荐巡检周期

周期 检查内容
每分钟 可连接性、磁盘、连接率、复制状态
每 5 分钟 长 SQL、锁等待、WAL 归档、复制槽
每小时 WAL 增量、检查点、临时文件、Timescale 任务
每日 备份结果、死元组、XID 年龄、数据新鲜度
每周 慢 SQL、容量增长、索引情况、角色变更
每月 完整恢复、PITR 演练、补丁评估
每季度 故障切换演练、权限审计、容量规划

真正的告警应该基于“单位时间变化”,例如:

  • 最近 5 分钟增加了多少 WAL。
  • 最近 1 小时发生多少归档失败。
  • 最近 24 小时数据库增长多少。
  • TimescaleDB 任务连续失败多少次。

只看累计总数容易误判。


十七、你的版本需要特别注意

如果当前仍是 PostgreSQL 18.3,截至 2026 年 9 月,官方当前维护版本已经是 PostgreSQL 18.6。18.6 修复了 28 个安全漏洞和 110 多个错误,因此建议尽快在恢复副本上验证 TimescaleDB、扩展和业务 SQL,然后安排小版本更新。PostgreSQL 18.6 发布说明

小版本升级通常:

  • 不需要 pg_upgrade
  • 不需要 pg_dump/pg_restore
  • 需要停止服务、更新二进制并重新启动。
  • 仍然必须提前完成备份和恢复验证。
  • 必须阅读被跳过版本的发布说明和特殊处理步骤。
  • 必须确认 TimescaleDB 及其他扩展包兼容。

升级前先记录:

SELECT version();

SELECT
    extname,
    extversion
FROM pg_extension
ORDER BY extname;

一套成熟的 PostgreSQL 运维,最终应形成闭环:

监控发现 → SQL 定位 → 控制影响 → 修复根因
→ 验证数据 → 记录复盘 → 更新告警和操作手册

如果有需要,可以设置“每周 PostgreSQL 运维巡检”,用于定期检查备份、膨胀、慢 SQL 和版本补丁。