postgres-howto 学习笔记

记录一些自己关注的知识点,不会每条都记,可能有部分补充,原文请看 postgres-howto

一、 查询性能与观测

1. 建议用 EXPLAIN (ANALYZE, BUFFERS)

可以看到SQL每步实际的IO次数(乘以8K则为字节数),pg 18开始,buffer成为默认值

2. pg_stat_statements速通

  • 用途:top sql 宏观分析
  • 范围:不包含在执行的语句、执行失败的语句
  • 指标:核心为累积指标(calls、total_time、rows 等),部分为统计类指标(stddev_plan_time、stddev_exec_time、min_exec_time)

完整指标:https://www.postgresql.org/docs/current/pgstatstatements.html

  • 怎么用:取快照,看两个快照间的差异数据。如果真的分析DB重启至今的数据,可以直接看
  • 如今AI已经非常强大,其实更重要的是知道要看什么:给它指标名,想看的数据,我们负责拿分析结果

3. 临时收集会话数据

  • 每秒收集一次pg_stat_activity样本,并无限次地记录日志 (直到手动中断)
  • 导出的csv文件,原文是导回pg分析,现在更好的方式,直接喂给AI获取目标结果
sql 复制代码
while sleep 1; do
    psql -XAtc "
        copy (
        with samples as (
            select
            clock_timestamp(),
            clock_timestamp() - xact_start as xact_duration,
            *
            from pg_stat_activity
        )
        select *
        from samples
        where xact_duration > interval '1 minute'
        order by xact_duration desc
        ) to stdout delimiter ',' csv
    " 2>&1 \
    | tee -a long_tx_$(date +%Y%m%d).log.csv
done

4. 什么查询叫慢查询

最近刚好也被业务同事问到是否有慢查询监控,多慢的语句算慢,会影响业务,其实应该是由业务定义,但作为DBA,可以给出一些建议范围:

OLAP:

  • 一般看与均值的差异,均值1小时,某次突发2小时,算慢
  • 有增量不用用全量,用增量但没索引,十有八九都算慢
  • 会有较频繁DDL的表,如果查询运行分钟至小时级,易高频阻塞业务,一般也算慢

OLTP:

  • 10ms以下:高性能
  • 100ms以下:推荐的性能
  • 100~200ms:若面向工厂流水线等,可以为慢查询
  • 1s:若面向用户,基本属于有感的慢查询

高频查询:

  • 即使是高性能查询,在过高执行频率下也会导致负载崩溃,需要注意

二、 运维排障

1. pg关闭慢的常见原因

  • 存在大事务/长事务
  • 大量缓冲区是脏的 ,导致关闭时的检查点时间过长
  • WAL 归档 (archive_command) 滞后
  • 从库延迟

之前停库也遇到过停不下来的问题,当时是归档异常及存在发送延迟,可以参考

PG fast模式停库 在归档过慢及有发送延迟 会被阻塞-CSDN博客

2. pg长时间无法启动如何处理

不该做的事情 (非专家常见的做法):

  • 不明所以就开始担心或等待很长时间
  • 多次尝试停止/重新启动

该做的事情:

  • 保持冷静

首先查看日志,了解它正在做什么

  • 了解你的配置和工作负载

配置:max_wal_size和checkpoint_timeout 主要是检查点相关配置,常见于强制关机或从备份恢复

工作负载:主要是wal日志的产生量

  • 了解并观察 REDO 进程

比较长,后续单独记一篇学习

3. 如何加速pg_dump

  • 压缩

pg_dump ... | gzip

  • 不保存到磁盘的转储/恢复

pg_dump -h ... | pg_restore

  • 并行化的 pg_dump

通过指定 -j数值 启动 指定数量的并行 pg_dump 进程来加速:

bash 复制代码
pg_dump -Fd -j8 -f ./test_dump test
  • 自定义的高级并行化

pg_dump 的并行化是在表级别进行的,无法并行转储单个表。

要并行转储单个大表,需要使用自定义解决方案。为此,我们需要使用多个 SQL 客户端,如 psql,每个客户端在 REPEATABLE READ 隔离级别下工作 (pg_dump 也是在此隔离级别下工作的,参见文档),且 (十分重要) 所有转储事务需要使用相同的快照。

4. 监控索引创建/重建进度(pg 12开始)

此查询的核心依赖于 pg_stat_progress_create_index(pg 12 引入)

PostgreSQL: Documentation: 17: 27.4. Progress Reporting

配合\watch 5命令,可以循环执行查看进度

sql 复制代码
select
   now(),
   query_start as started_at,
   now() - query_start as query_duration,
   format('[%s] %s', a.pid, a.query) as pid_and_query,
   index_relid::regclass as index_name,
   relid::regclass as table_name,
   (pg_size_pretty(pg_relation_size(relid))) as table_size,
   nullif(wait_event_type, '') || ': ' || wait_event as wait_type_and_event,
   phase,
   format(
           '%s (%s of %s)',
           coalesce((round(100 * blocks_done::numeric / nullif(blocks_total, 0), 2))::text || '%', 'N/A'),
           coalesce(blocks_done::text, '?'),
           coalesce(blocks_total::text, '?')
   ) as blocks_progress,
   format(
           '%s (%s of %s)',
           coalesce((round(100 * tuples_done::numeric / nullif(tuples_total, 0), 2))::text || '%', 'N/A'),
           coalesce(tuples_done::text, '?'),
           coalesce(tuples_total::text, '?')
   ) as tuples_progress,
   current_locker_pid,
   (select nullif(left(query, 150), '') || '...' from pg_stat_activity a where a.pid = current_locker_pid) as current_locker_query,
   format(
           '%s (%s of %s)',
           coalesce((round(100 * lockers_done::numeric / nullif(lockers_total, 0), 2))::text || '%', 'N/A'),
           coalesce(lockers_done::text, '?'),
           coalesce(lockers_total::text, '?')
   ) as lockers_progress,
   format(
           '%s (%s of %s)',
           coalesce((round(100 * partitions_done::numeric / nullif(partitions_total, 0), 2))::text || '%', 'N/A'),
           coalesce(partitions_done::text, '?'),
           coalesce(partitions_total::text, '?')
   ) as partitions_progress,
   (
      select
         format(
                 '%s (%s of %s)',
                 coalesce((round(100 * n_dead_tup::numeric / nullif(reltuples::numeric, 0), 2))::text || '%', 'N/A'),
                 coalesce(n_dead_tup::text, '?'),
                 coalesce(reltuples::int8::text, '?')
         )
      from pg_stat_all_tables t, pg_class tc
      where t.relid = p.relid and tc.oid = p.relid
   ) as table_dead_tuples
from pg_stat_progress_create_index p
        left join pg_stat_activity a on a.pid = p.pid
order by p.index_relid
; -- in psql, use "\watch 5" instead of semicolon

未完待续...

相关推荐
only-lucky1 小时前
QML深入学习四(布局用法)
学习
蜗牛^^O^2 小时前
Agent学习笔记
笔记·学习
小郑加油2 小时前
python学习Day13:实际应用——pandas 进阶计算
python·学习·pandas
ps酷教程3 小时前
jackson学习
java·学习
问心无愧05133 小时前
ctf show web入门90
前端·笔记
Mike117.3 小时前
GBase 8c 里 search_path 没管住,SQL 可能跑到另一个对象上
数据库·sql·postgresql
NULL指向我3 小时前
STM32 F103C8T6学习笔记20:SPI驱动W25Qxx
笔记·stm32·学习
吃好睡好便好3 小时前
汽车行驶原理
学习·汽车
吃好睡好便好3 小时前
Matlab中三种三维图的对比
开发语言·人工智能·学习·算法·matlab·信息可视化