AtomSlice

独立开发者

中文技术深度文 → 英文 X 长推串

把一篇三千字的中文技术长文改写成英文 X 长推:一条一个机制,代码与参数原样保留。

· 实战案例 ·English

示例演示:以下场景与数字为说明用,不是客户结果,也不代表效果承诺。

改写前 → 改写后

改写前

一篇中文长文,中文圈之外没人读得到

语言
中文
读者
只有中文读者

改写后

一份可以直接发布的英文资产

语言
English
读者
英文买家与从业者
产出
7 条长推

01进来的东西

原文(中文)

PostgreSQL 的 MVCC 会把死元组一直留着,直到 VACUUM 把它们清掉;写入一快,死元组的堆积速度就超过清理速度,表于是越写越大。

核心论点:PostgreSQL MVCC 保留死元组直到 VACUUM 清理。高写入吞吐下,死元组产生速度 > autovacuum 清理速度 → 表膨胀 → 索引膨胀 → 膨胀索引上的全表扫描 → 延迟飙升。

关键数字:

  • 4000 万行表,2000 写入/秒
  • Autovacuum 触发阈值:20% 死元组
  • 实际膨胀:表 340%、索引 280%
  • P99 延迟:12ms → 2400ms

应用方案:autovacuum_vacuum_scale_factor = 0.01、autovacuum_vacuum_cost_limit = 10000、为热点查询加部分索引。

02一篇文章拆出的五个角度

「大家都说『PostgreSQL 自动处理并发』。数据却说:4000 万行、2000 写入/秒,默认 autovacuum 直接失效。这是修复方案。」
  • 打击普遍迷思(「PostgreSQL 开箱即用」)
  • 开篇亮硬核失效数据(非理论推演)
  • 承诺具体可复现的修复动作
AtomSlice 创建页右侧的「跨市场语境映射」面板,识别出 3 处需要换角度的表述
提取出的角度 · AtomSlice

03产出的东西

Tweet 1 (Hook)

PostgreSQL's "automatic" vacuum is a lie at scale.

40M rows. 2K writes/sec. Default settings → 340% table bloat. P99 latency 12ms → 2,400ms.

Your database isn't broken. Your autovacuum config is.

🧵 7 tweets on the fix that saved our production DB.

Tweet 2 (The Mechanism)

MVCC keeps every dead row version until VACUUM reclaims it.

High write throughput = dead tuples accumulate faster than autovacuum wakes up.

Default trigger: 20% dead tuples. At 2K writes/sec, you hit 340% bloat before autovacuum even starts its second cycle.

Tweet 3 (The Symptom)

What 340% bloat looks like:

  • Table: 40GB → 136GB (dead tuples)
  • Indexes: 8GB → 22GB (dead pointers)
  • Sequential scans on bloated indexes = CPU at 100%, P99 at 2.4s

Your monitoring shows "high CPU." The root cause is vacuum debt.

Tweet 4 (The Fix — Config)

Three settings that changed everything:

autovacuum_vacuum_scale_factor = 0.01    -- trigger at 1%, not 20%
autovacuum_vacuum_cost_limit = 10000     -- 10x default IO budget
autovacuum_max_workers = 4               -- parallel vacuum (if cores allow)

Restart PostgreSQL. Watch pg_stat_progress_vacuum.

Tweet 5 (The Fix — Indexes)

Partial indexes for hot queries > full indexes on bloated tables.

-- Before: full index on 40M rows (22GB with bloat)
CREATE INDEX ON events (user_id, created_at);

-- After: partial index on last 30 days (2.1GB, 95% of queries)
CREATE INDEX ON events (user_id, created_at) 
WHERE created_at > NOW() - INTERVAL '30 days';

Query latency: 2,400ms → 8ms. Index size: 22GB → 2.1GB.

Tweet 6 (The Monitoring)

Add these to your dashboards before you need them:

-- Bloat ratio (run weekly)
SELECT schemaname, tablename, 
       pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) as total,
       pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename) - pg_relation_size(schemaname||'.'||tablename)) as bloat
FROM pg_tables WHERE schemaname = 'public';

-- Autovacuum lag
SELECT * FROM pg_stat_progress_vacuum;

Tweet 7 (CTA + Takeaway)

Default autovacuum = "good enough for hobby projects."

Production at scale = you own the vacuum strategy.

The fix isn't magic. It's respecting the physics: dead tuples don't delete themselves. You configure the reaper, or the reaper buries you.


同一套动作可以复用

  1. 01
    用失败的数字开场,不要用解法开场「膨胀 340%」能让人停下来;那段配置是回报,不是钩子。
  2. 02
    把改变结论的代码块原样留下读者会转发可直接复现的配置,不会转发摘要。
  3. 03
    一条推只讲一个概念MVCC、症状、配置修复、索引修复、监控——五个节拍、七条推,每条都不挤。
  4. 04
    把你加上的监控写出来没法验证是否修好的修复,只是格式更漂亮的传言。
  5. 05
    翻译结论,不翻译行文`autovacuum_vacuum_scale_factor` 照原样写;只重写它周围的叙述。
  6. 06
    结尾落在机制上,不落在 CTA 上「死元组不会自己消失」是别人会引用回来的那句。