代表性面试主题

如何在线安全地创建或重建 PostgreSQL 索引?

后端困难
OfferCC 编辑团队发布 更新

题干

一张数十亿行的 PostgreSQL 表需要新增索引或重建膨胀索引,但业务不能停机。请设计执行步骤、失败恢复和验证指标。

1. 题目

生产表持续接收订单写入,查询团队希望新增复合索引;另一张表的索引膨胀,需要重建。表规模很大,发布窗口只有低峰期,且不能阻塞普通读写。请给出可回滚、可观测的在线方案。

2. 约束与澄清

  • 先确认 PostgreSQL 版本、表和索引类型、主从拓扑、磁盘余量及写入峰值。
  • 区分 CREATE INDEX、CREATE INDEX CONCURRENTLY、REINDEX CONCURRENTLY 的适用场景。
  • 并发构建减少写锁影响,但会扫描表多次、占用 CPU/IO,并且不能放在事务块中。
  • 先定义可接受的构建时长、锁等待、延迟和失败后的清理窗口。

3. 核心思路

普通建索引可能阻塞写入;并发建索引允许持续插入、更新和删除,但需要更长时间和更多资源。应先在影子环境用真实规模估算,生产执行时设置 lock_timeout、statement_timeout 和资源监控。索引建成后还要确认规划器选择、回滚路径和副本一致性,不能把命令成功当作业务成功。

4. 参考流程

text
preflight:
  verify_version_replicas_disk_and_query_shape()
  estimate_scan_cost_on_shadow_copy()
  reserve_maintenance_window_and_abort_thresholds()

build:
  set lock_timeout = short
  set statement_timeout = bounded
  CREATE INDEX CONCURRENTLY idx_orders_customer_time
    ON orders (customer_id, created_at DESC)

verify:
  inspect_index_state_and_size()
  EXPLAIN (ANALYZE, BUFFERS) representative_queries()
  compare_write_latency_replica_lag_and_error_rate()

重建时优先使用 REINDEX CONCURRENTLY;若失败留下无效的临时索引,先按文档识别并清理,再重新尝试。部署脚本要保证命名、幂等和告警可追踪,不能把并发 DDL 藏在普通事务迁移里。

5. 失败场景与取舍

并发构建可能因长事务、冲突快照或磁盘不足失败;失败对象可能保持无效状态,继续占用空间。构建期间 CPU、IO 和 WAL 增长会拖慢业务并拉大复制延迟,因此要限速或暂停。若无法接受构建成本,可以先优化查询、分区或使用在线迁移工具,但工具仍需验证触发器、回填、切换和回滚行为。

6. 验证与观测

  • 记录索引状态、大小、构建耗时、锁等待、WAL、CPU/IO 和副本延迟。
  • 对代表性查询比较执行计划、扫描行数、p95/p99 延迟和写入吞吐。
  • 检查长事务、无效索引、重复索引及约束依赖。
  • 观察多个完整业务高峰后再删除旧索引,并保留恢复脚本。

7. 常见误区

  • 认为 CONCURRENTLY 完全不拿锁,忽略短暂锁等待和资源竞争。
  • 把 CREATE INDEX CONCURRENTLY 放进事务块,导致命令直接失败。
  • 只看建索引命令返回成功,不检查无效索引、复制延迟和实际计划。
  • 在没有磁盘、WAL 和长事务预算时直接对超大表重建。

8. 面试评分点

能区分并发 DDL 语义

应说明普通和并发创建、重建索引的锁、扫描次数、事务限制与资源代价。

能规划生产前置检查

应检查版本、磁盘、长事务、复制拓扑、查询形状,并先用接近真实规模的数据估算。

能设计失败恢复

应处理无效索引、超时、磁盘不足和复制落后,说明清理、重试与回滚步骤。

能用业务指标验证

应比较执行计划、p95/p99、写入延迟、WAL、锁等待和副本延迟,而不是只看 DDL 返回码。

公开来源

同类题目

资源

产品对比

每份对比均基于官方资料,并标注复核日期。

查看完整对比