ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

PostgreSQL 高频面试错题复盘:12 个最容易答错的机制与生产问题

PostgreSQL 高频面试错题复盘:12 个最容易答错的机制与生产问题 核心判断PostgreSQL 面试最容易失分的不是记不住术语而是把局部事实说成完整结论。结论与条件 → 被漏掉的状态 → 触发与内部机制 → 可观察证据 → 代价与边界 → 生产动作唯一键能去重不等于能裁决新旧Index Only Scan 能从索引取列不等于一定不访问 heap物理副本追平也不等于所有 WAL 消费者都正常。这不是概念题清单而是一份错题复盘先看 30 秒答案再按需要进入完整博客案例。阅读地图并发与正确性01—04— 约束、MVCC、Write Skew 与 Upsert 新旧裁决。存储与维护05—08— Visibility Map、空间回收、Autovacuum 与长快照。计划与资源09—12— 基数估算、计划缓存、WAL 保留与内存放大。一页错题索引题号高频错题最常见错答一句话纠正深度证据01并发预约怎样保证不重叠先查再插或者加唯一键跨时间范围的不变量必须进入数据库约束、锁或可序列化冲突检测排他约束与并发不变量02UPDATE 为什么留下两个行版本PostgreSQL 会原地覆盖MVCC UPDATE 通常创建新 tuple 并让旧 tuple 失效HOT 也不是原地更新Heap Tuple 与 HOT03Repeatable Read 为什么仍会写偏差可重复读已经解决并发问题两个事务可以修改不同行却共同破坏跨行不变量SSI 才检测危险结构Write Skew 与 SSI04ON CONFLICT 为什么仍会旧状态覆盖新状态Upsert 成功就代表幂等且最新唯一键只裁决身份冲突不裁决事件先后更新条件必须显式比较业务版本Upsert 新旧裁决05Index Only Scan 为什么还回表覆盖索引一定不访问 heap索引解决列可得性Visibility Map 的 all-visible 位才决定能否跳过 heap 可见性检查覆盖索引与 Visibility Map06DELETE 后磁盘为什么不下降VACUUM 会把空间还给操作系统普通 VACUUM 主要让 relation 内部空间可复用文件缩小还受尾部空页、锁和重写条件约束DELETE、VACUUM 与文件尺寸07Autovacuum 在跑为什么表仍膨胀进程存在就说明清理正常是否追上要看死元组净斜率、回收 horizon、worker 排队和清理吞吐Autovacuum 债务08只读事务为什么能拖垮整库不写数据也不持锁所以无害长快照可通过backend_xmin抬住可见性 horizon使旧版本无法回收长事务与回收边界09SQL 与索引没变为什么计划突然变慢一定是缓存、网络或数据库抖动先找 estimated/actual rows 的第一处分叉统计误差会放大为错误 Scan、Join 和 loops统计信息与基数估算10预编译 SQL 第六次为什么变慢第六次必定切 generic plan前五次后只是开始比较 generic 与平均 custom 估算成本是否切换仍由成本决定Custom 与 Generic Plan11复制延迟为零为什么pg_wal仍增长副本追平就没有 WAL 积压物理复制、逻辑槽和归档是不同消费链旧restart_lsn仍可阻止 WAL 回收复制槽与 WAL 保留12work_mem调大为什么更容易 OOMwork_mem是每条查询的内存上限它是操作级基础预算多个节点、Hash 倍数、并行参与者和并发查询会叠加work_mem与实例峰值01并发前先查一次为什么挡不住重复预约❌ 常见错答先SELECT检查没有冲突再INSERT或者给会议室加唯一键。✅ 30 秒核心回答两个事务可能同时读到无冲突再分别提交重叠时间段。若规则是同一会议室的时间范围不能重叠优先用 range type 配合 exclusion constraint 在并发提交时裁决约束难以表达时再评估守卫行锁或 Serializable。应用预检查只能改善提示不能充当最终防线。 机制与边界用两个会话插入重叠的tstzrange对照有无排他约束时的提交结果。排他约束会增加索引维护和冲突等待Serializable 需要应用重试40001。 深度文章第 1 篇排他约束如何挡住并发重复预约02HOT 为什么仍然不是原地 UPDATE❌ 常见错答HOT update 会直接改原 tuple所以不会产生死版本。✅ 30 秒核心回答UPDATE 通常写入新 tuple并让旧 tuple 对后续快照失效。新版本能放在同一 heap page 且修改列不被索引引用时才可能形成 HOT 链减少索引 entry 维护旧 tuple 仍需 VACUUM 回收。 机制与边界对照pg_stat_all_tables.n_tup_upd、n_tup_hot_upd和pageinspect中的 tuple header。fillfactor 只提供页内空间不保证 HOT。 深度文章第 2 篇一次 UPDATE 为什么留下两个行版本03两行没有写冲突为什么值班医生一个也没剩❌ 常见错答Repeatable Read 已解决并发问题或者锁住医生行就够了。✅ 30 秒核心回答两个事务可以读取相同集合却更新不同的行最终共同破坏至少一人值班的跨行不变量这就是 write skew。PostgreSQL Serializable 使用 SSI 记录读写依赖并识别危险结构必要时中止一个事务应用必须重试整个事务。 机制与边界在 Repeatable Read 和 Serializable 下重放同一双会话时序对照最终结果与40001。若存在稳定守卫行显式行锁可能更简单锁当前结果集不一定覆盖未来插入。 深度文章第 3 篇两行都没写错值班医生为什么一个也没剩04ON CONFLICT 成功为什么仍然不是新状态获胜❌ 常见错答唯一键加ON CONFLICT DO UPDATE已经实现消息幂等。✅ 30 秒核心回答唯一键只裁决对象身份不裁决事件先后。必须用业务版本、源端 LSN 或单调序列作为更新条件只有 incoming version 更大时才更新删除还要保留 tombstone 或版本水位避免旧更新让对象复活。 机制与边界依次写入版本 105 和 103对照无条件 Upsert 与带版本WHERE的结果。事务原子性仍不等于消息 Exactly-Once。 深度文章第 4 篇ON CONFLICT 两次都成功旧订单为什么覆盖新状态05有覆盖索引Index Only Scan 为什么还有 Heap Fetches❌ 常见错答查询字段都在索引里Index Only Scan 就完全不访问表。✅ 30 秒核心回答Index Only Scan 既要求索引能返回所需列也要求目标 heap page 在 Visibility Map 中被标记为 all-visible。INCLUDE只解决“列能否从索引取得”UPDATE/DELETE 会清除可见性位VACUUM 满足条件后才可能重新设置。 机制与边界用EXPLAIN (ANALYZE, BUFFERS)对照 VACUUM 前、VACUUM 后、UPDATE 后和再次 VACUUM 后的Heap Fetches。即使执行节点名没有变化回表次数也可能明显变化盲目追加宽列还会增加索引体积、缓存压力和写放大。 深度文章第 5 篇Index Only Scan 为什么仍然回表一万次06DELETE 清空九成数据为什么文件几乎没缩小❌ 常见错答DELETE 后再执行 VACUUM释放的磁盘就会立即归还操作系统。✅ 30 秒核心回答“行逻辑不可见”“relation 内部空间可复用”和“文件物理缩小”是三个不同结果。普通 VACUUM 主要让空间可供同一 relation 后续复用只有尾部空页连续、锁条件满足等情况下才可能截尾。VACUUM FULL、CLUSTER等重写可收缩文件但会消耗额外空间并带来更强的锁影响。 机制与边界同时观察pg_relation_size()、pg_total_relation_size()、dead tuple以及后续 INSERT 是否复用空间而不继续增长文件。若数据天然按时间到期优先评估分区生命周期管理而不是周期性依赖全表重写。 深度文章第 6 篇DELETE 清空九成数据磁盘为什么几乎没变07Autovacuum 一直在跑为什么死元组还在增加❌ 常见错答pg_stat_progress_vacuum里有任务就说明 Autovacuum 清理正常。✅ 30 秒核心回答任务存在不代表清理吞吐大于死元组产生速度也不代表旧版本已经越过回收 horizon。必须同时检查触发阈值、worker/slot 排队、cost delay、索引清理阶段、长事务或复制槽阻塞以及 dead tuple 的时间序列净斜率。 机制与边界联查pg_stat_progress_vacuum、pg_stat_activity.backend_xmin、pg_replication_slots和表级统计判断问题是“没有及时触发”“执行速度不足”还是“受 horizon 阻塞”。单纯提高 worker 数可能只会把瓶颈转移到 I/O。 深度文章第 7 篇Autovacuum 一直在跑死元组为什么越积越多08只读长事务没有锁等待为什么也能拖胖整库❌ 常见错答只读事务不修改数据所以不会影响 VACUUM。✅ 30 秒核心回答VACUUM 判断旧 tuple 能否回收不只看锁还要看它是否可能被任何存活快照看到。长快照可通过backend_xmin抬住可见性 horizon使并发 UPDATE/DELETE 产生的旧版本暂时无法回收因此“只读”和“无锁等待”都不等于“对清理无影响”。 机制与边界让一个会话保持旧快照另一个会话反复更新并执行 VACUUM再对照长事务提交前后的回收结果。排查时还要区分 prepared transaction、逻辑复制槽与hot_standby_feedback因为它们都可能以不同路径抬住回收边界。 深度文章第 8 篇没有锁等待只读事务为什么仍能拖胖整库09SQL 没变为什么执行计划能慢一百倍❌ 常见错答索引和 SQL 都没改性能突然变差只能是缓存、网络或硬件抖动。✅ 30 秒核心回答SQL 文本不变不代表优化器输入不变。数据分布、统计样本、参数值和相关性都会改变基数估算估算一旦在底层节点发生数量级偏差Join 顺序、Join 算法和访问路径就可能连锁变化。 机制与边界先用EXPLAIN (ANALYZE, BUFFERS)从下往上找 estimated rows 与 actual rows 的第一处数量级分叉再检查统计目标、列相关性和扩展统计。对强相关列比较创建CREATE STATISTICS ... (dependencies, mcv)前后的估算与计划临时禁用某种 Join 只能用于反证不能作为根因修复。 深度文章第 9 篇SQL 和索引没变计划为什么突然慢一百倍10第六次执行 prepared statement为什么不一定切 generic❌ 常见错答PostgreSQL 前五次使用 custom plan第六次一定切换成 generic plan。✅ 30 秒核心回答五次只是启发式采样门槛不是固定切换规则。choose_custom_plan()会比较 generic plan cost 与平均 custom plan costcustom 侧还会计入重复规划代价只有比较结果支持 generic后续才会选用它。因此准确说法是第六次开始“可能”发生变化。 机制与边界在同一会话对相同 prepared statement 分别强制 custom/generic比较计划、Buffers 和耗时再观察pg_prepared_statements.generic_plans、custom_plans的前后增量。客户端驱动的 prepare 阈值、连接池是否复用同一 session也会直接影响实验是否进入服务端决策路径。 深度文章第 10 篇预编译 SQL 前五次都快第六次为什么可能变慢11物理副本追平为什么闲置逻辑槽仍会撑爆 pg_wal❌ 常见错答replay_lag 0说明所有复制链路都正常max_wal_size会限制pg_wal目录大小。✅ 30 秒核心回答物理副本、逻辑复制槽和归档是三条不同链路。物理副本追平不能证明逻辑消费者已经推进逻辑槽即使active false旧restart_lsn仍可能要求主库继续保留 WAL。max_wal_size不是硬上限不能覆盖复制槽的保留需求。 机制与边界联查pg_stat_replication、pg_replication_slots、pg_ls_waldir()和pg_stat_archiver把“谁在保留 WAL”定位到具体链路。max_slot_wal_keep_size可限制槽的保留量但代价是槽可能失效删除生产槽前必须确认负责人、业务位点、快照能力和下游对账方案。 深度文章第 11 篇主从延迟为零闲置逻辑槽为什么仍能撑爆 pg_wal12work_mem 从 4MB 调到 256MB风险为什么不只放大 64 倍❌ 常见错答work_mem是每条查询的内存上限用max_connections × work_mem就能算出实例峰值。✅ 30 秒核心回答work_mem面向 Sort、Hash 等执行操作而不是整条查询的一次性总额度。一条计划可能同时出现多个相关节点Hash 还受hash_mem_multiplier影响并行 worker 与并发查询会继续叠加。因此内存风险不是简单的连接数乘法。 机制与边界对同一查询分别设置 4MB 与 256MB比较 Hash batches、Sort Disk、temp I/O 和耗时再在受控隔离环境逐级增加并发观察进程 RSS、swap 与 OOM 风险。MemoryContext 快照不是历史峰值治理上应先修错误计划再对受控角色或事务局部提高预算。 深度文章第 12 篇work_mem 只调大 64 倍峰值为什么远不止 64 倍面试官继续追问时别掉进这四个坑不要把现象当根因。Heap Fetches高、dead tuple 多、WAL 目录大都只是信号还要继续定位决定状态的机制。不要把一次截图当趋势。Autovacuum、WAL、内存和复制问题必须看时间序列、变化率与预计耗尽时间。不要把止血当根治。重连、DEALLOCATE、扩大磁盘、提高work_mem可能暂时改变现象但不会自动修复数据模型、消费链或容量边界。不要承诺绝对安全。正确答案必须带版本、并发、数据分布、权限、锁影响、重试或重建条件。PostgreSQL 18.6 资料基线PostgreSQL 18.6 Release NotesPostgreSQL 18Concurrency ControlPostgreSQL 18Routine VacuumingPostgreSQL 18Using EXPLAINPostgreSQL 18Replication SlotsPostgreSQL 18Resource ConsumptionPostgreSQL 18.6 源码标签 REL_18_6
返回列表