← 中文资源

简体中文版本

PostgreSQL 部分索引与软删除

用部分索引只覆盖未删除的活行:热查询的索引体积与活数据成正比,软删除行不进入索引,唯一约束也按活行生效。

阅读英文原文

示例说明

软删除表里九成以上可能是已删除的历史行——全量索引把这些死数据全背着,每次查询都在放大体积里捞小集合。部分索引的 WHERE 谓词把索引内容限定在活行:体积随热数据缩放,B 树更浅查找更快,VACUUM 回收死行的负担也更小。唯一约束同理:同标题唯一只约束未删除文档。

技术要点

  • 索引谓词限定只收活行
  • 查询条件须与谓词精确匹配
  • 唯一性按活行集合生效

代码解读

两个索引都以 deleted_at IS NULL 为谓词:复合索引服务按所有者查活跃文档的主路径,排序键内建让 ORDER BY 免排序;唯一索引把软删除的老标题从约束里排除——重新创建同名文档不会被自己删除过的行卡住。归档查询明确不匹配谓词,自动走其他计划。

使用建议

部分匹配的谓词不会被采用——写查询时逐字对齐索引条件。定期物理归档已删除行防表无限膨胀。谓词变动需要重建索引。

代码示例

-- PostgreSQL 软删除表:业务查询永远附加 deleted_at IS NULL
CREATE TABLE documents (
  id bigserial PRIMARY KEY,
  owner_id bigint NOT NULL,
  title text NOT NULL,
  deleted_at timestamptz
);

-- 部分索引只覆盖活行:体积与热数据成正比
CREATE INDEX idx_documents_owner_live
  ON documents (owner_id, created_at DESC)
  WHERE deleted_at IS NULL;

-- 命中部分索引:条件必须与索引谓词精确匹配
SELECT id, title
FROM documents
WHERE owner_id = 42
  AND deleted_at IS NULL
ORDER BY created_at DESC
LIMIT 10;

-- 归档查询走另一条路:不占热索引的空间
SELECT id, deleted_at
FROM documents
WHERE owner_id = 42
  AND deleted_at IS NOT NULL
ORDER BY deleted_at DESC
LIMIT 100;

-- 唯一性也按活行生效:同 owner 的活文档标题唯一
CREATE UNIQUE INDEX idx_documents_title_live
  ON documents (owner_id, title)
  WHERE deleted_at IS NULL;