perf(db): 干掉列表/计数热路径上的「整表拼单聚合」,并给数据库留出 1 核
生产实测(2026-10-03,order_site):postgres 在 22.6 小时里烧了 12.2 CPU·小时 (均 0.54 核),却因 2 核配额累计被 cgroup 节流 9,337 秒;top 12 条语句占全部 DB CPU 的 69.4%,其中三条最热的就是「工单列表 / 计数 / 大厅候选集」。 一、查询形状(根因,不是缺索引) 接单大厅那条 COUNT 被调 71,539 次、均 103ms,EXPLAIN(ANALYZE,BUFFERS) 显示 82.8ms 里 35ms 花在「把整张 work_order_shares 聚合一遍再 join」: Index Only Scan rows=22,987 → GroupAggregate → 最终只用到 11 行;同一条 SQL 里 还有第二遍同样的全局聚合(filter_share_summary)。已改成按工单限定的关联子查询 (走 idx_work_order_shares_work_order): - buildWorkOrderWhere 的 excludeFilledSharing 过滤条件; - buildHallCandidateIdsSql 的 candidate_shares。 同参数实测:50.3ms → 2.1ms、35.9ms → 3.8ms、28.4ms → 1.8ms,buffer 10,145 → 187, 三种参数下结果逐行一致(11/11、5/5、11/11)。 有意保留 operational_share_summary 那一处全局聚合:全表统计(103,073 行)时 关联子查询要 103,073 次探测(330ms),一次聚合只要 55ms;已有用例钉住「只允许剩一处」。 二、容器与数据库参数 - POSTGRES_CPUS 2 → 3(4 核机器,同机其它容器实测只用 0.2-0.4 核且几乎不节流); - max_parallel_workers_per_gather 2 → 1(>1 时一条查询要 leader+2 worker=3 核,必撞配额); - checkpoint_timeout 300 → 900s、max_wal_size 1GB → 4GB(43 天累计 21,778 次检查点, sar 里就是每 20-40 分钟一轮写突发)。 三、迁移 092:热表 autovacuum 调参 可见性图陈旧导致 index-only scan 回表 5,580 次;work_orders/order_items/orders/ kuaishou_industry_vouchers/fulfillment_tasks 死元组 1.9 万-5.3 万、上次清理在前一天 甚至 9/29。把这些表的 scale_factor 0.2→0.05、analyze 0.1→0.02、cost_limit 200→800, 把「攒批猛扫」拆成「小步勤清」,并打开 5s 以上的 autovacuum 日志便于观测。 用例:新增 share-summary-scope.test.ts(钉住热路径不再出现整表聚合、只保留一处且有注释说明), env-documentation 白名单补 3 个新增部署变量。全量 599 通过,typecheck/lint/check:sql 通过。
This commit is contained in:
+16
-1
@@ -26,11 +26,26 @@ services:
|
||||
"work_mem=${POSTGRES_WORK_MEM:-16MB}",
|
||||
"-c",
|
||||
"maintenance_work_mem=${POSTGRES_MAINTENANCE_WORK_MEM:-256MB}",
|
||||
# 并行度 1:生产实测单条大查询一旦开 2 个 parallel worker,就需要
|
||||
# 「leader + 2 worker = 3 核」,而容器配额只有 2 核 → 必然撞配额被节流
|
||||
# (累计 nr_throttled 94,197 次 / throttled_usec 9,337s)。重查询已优化到
|
||||
# 10ms 量级,用不到并行。
|
||||
"-c",
|
||||
"max_parallel_workers_per_gather=${POSTGRES_MAX_PARALLEL_WORKERS_PER_GATHER:-1}",
|
||||
# 检查点:默认 5 分钟一次 + max_wal_size 1GB,生产 43 天累计 21,778 次检查点,
|
||||
# sar 里就是每 20–40 分钟一轮写突发(wtps 130、8MB/s)。放宽到 15 分钟 / 4GB。
|
||||
"-c",
|
||||
"checkpoint_timeout=${POSTGRES_CHECKPOINT_TIMEOUT:-900}",
|
||||
"-c",
|
||||
"max_wal_size=${POSTGRES_MAX_WAL_SIZE:-4GB}",
|
||||
]
|
||||
restart: unless-stopped
|
||||
# 让 PostgreSQL 优雅关闭有足够时间完成检查点;无限制时 OOM 可能拖垮整机。
|
||||
mem_limit: ${POSTGRES_MEM_LIMIT:-1.5g}
|
||||
cpus: ${POSTGRES_CPUS:-2}
|
||||
# 3 核:这台机器 4 核,postgres 是最大单一消耗者(22.6h 烧 12.2 CPU·h、均 0.54 核),
|
||||
# 却被 2 核配额卡住,累计 2.6 小时处于被节流状态。同机其它容器实测只用 0.2–0.4 核,
|
||||
# 且几乎不节流,所以这里让出 1 核给数据库的突发。
|
||||
cpus: ${POSTGRES_CPUS:-3}
|
||||
shm_size: 512mb
|
||||
stop_grace_period: 60s
|
||||
logging: *json-log-rotation
|
||||
|
||||
Reference in New Issue
Block a user