TIN:把全文检索带回 PostgreSQL 的索引选择

2026-09-16 26 预计阅读时间: 1 分钟
来源: planetscale.com AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:12 分钟

全文检索往往会把系统拆成两套基础设施:业务数据放在 PostgreSQL,搜索索引交给另一套专用服务。TIN 提供了另一条路线:在 PostgreSQL 内使用一个快速、功能完整的全文检索索引,把搜索能力尽量留在现有的数据边界里。

这并不意味着所有项目都应该立刻替换现有搜索系统。更实际的做法是:先明确搜索需求,再用可重复的 SQL 和压测验证 TIN 是否能满足延迟、相关性、更新速度与运维约束。

为什么 PostgreSQL 内的全文检索值得关注

当文档、权限、租户和业务状态都保存在 PostgreSQL 中时,外部搜索服务通常会带来额外工作:

  • 维护数据同步链路,以及处理最终一致性;
  • 在数据库查询和搜索结果之间重新实现权限过滤;
  • 监控另一套集群、索引构建流程和容量规划;
  • 处理删除、更新和批量导入时的重试与补偿。

TIN 的定位是全文检索索引,而不是一个独立的搜索平台。它的价值在于让开发者可以继续使用 PostgreSQL 的数据模型、事务边界和 SQL 查询方式,同时获得更适合全文搜索的索引能力。

这里需要区分两个概念:

  1. 索引能力:如何建立倒排结构、处理文本更新并加速匹配。
  2. 搜索产品能力:如何做联想词、拼写纠错、分布式扩展、分析报表和多语言词典管理。

TIN 主要解决的是第一类问题。是否还需要独立搜索服务,取决于项目是否需要第二类能力,以及数据规模和查询形态。

先把搜索契约写清楚

在接入任何新的索引之前,建议先固定搜索接口的行为。至少应明确以下问题:

  • 搜索哪些字段,标题和正文是否需要不同权重;
  • 是否支持短语、布尔条件、前缀或模糊匹配;
  • 结果如何排序,是相关性优先还是时间优先;
  • 租户、可见性、状态等过滤条件是否必须和全文匹配一起执行;
  • 文档更新后,多久必须能被搜索到;
  • 删除或权限变更后,旧内容最多允许暴露多久。

这一步很重要。一个索引即使查询速度很快,如果无法稳定满足权限过滤或更新可见性,也不能算作可用的搜索方案。

用 PostgreSQL 基线验证你的需求

下面的例子使用 PostgreSQL 原生全文检索建立一个可运行的基线。它不是 TIN 的安装或 DDL 示例,而是一个可以在接入 TIN 前后复用的测试契约:相同的数据、查询和过滤条件,可以用来比较延迟、结果质量与更新行为。

运行前只需要准备一个 PostgreSQL 数据库。示例使用 docker compose 启动数据库:

services:
  postgres:
    image: postgres:16
    environment:
      POSTGRES_USER: app
      POSTGRES_PASSWORD: app
      POSTGRES_DB: search_demo
    ports:
      - "5432:5432"

启动后执行以下 SQL:

CREATE TABLE documents (
    id          bigserial PRIMARY KEY,
    tenant_id   bigint NOT NULL,
    title       text NOT NULL,
    body        text NOT NULL,
    status      text NOT NULL DEFAULT 'published',
    updated_at  timestamptz NOT NULL DEFAULT now(),
    search_text tsvector GENERATED ALWAYS AS (
        setweight(to_tsvector('simple', coalesce(title, '')), 'A') ||
        setweight(to_tsvector('simple', coalesce(body, '')), 'B')
    ) STORED
);

CREATE INDEX documents_search_gin
    ON documents USING gin (search_text);

INSERT INTO documents (tenant_id, title, body) VALUES
    (10, 'PostgreSQL 索引设计', '讨论全文搜索、过滤条件和查询计划。'),
    (10, '数据库迁移手册', '介绍在线变更、回滚策略和数据校验。'),
    (20, '搜索服务运维', '记录索引重建、监控指标和故障排查。');

SELECT
    id,
    title,
    ts_rank(search_text, websearch_to_tsquery('simple', '全文搜索')) AS score
FROM documents
WHERE tenant_id = 10
  AND status = 'published'
  AND search_text @@ websearch_to_tsquery('simple', '全文搜索')
ORDER BY score DESC, updated_at DESC
LIMIT 20;

实际接入 TIN 时,可以保留这组输入与验收条件,再按照 TIN 的安装方式和索引定义替换索引部分。不要只比较一条简单查询的平均耗时,还应覆盖以下场景:

  • 单词搜索、多个词搜索和短语搜索;
  • 带租户和状态过滤的查询;
  • 批量导入后查询;
  • 高频更新和删除;
  • 冷缓存与热缓存;
  • 小结果集和深分页。

评估 TIN 时要看哪些指标

查询延迟

至少记录 p50、p95 和 p99,而不是只看平均值。全文检索通常服务于用户请求,长尾延迟会直接影响页面响应和 API 超时率。

可以把查询样本保存成固定集合,然后重复执行。例如:

#!/usr/bin/env bash
set -euo pipefail

queries=(
  "全文搜索"
  "数据库 索引"
  "在线 迁移"
  "监控 故障排查"
)

for q in "${queries[@]}"; do
  echo "=== $q ==="
  psql "$DATABASE_URL" -v q="$q" -c \
    "EXPLAIN (ANALYZE, BUFFERS, TIMING)
     SELECT id, title
     FROM documents
     WHERE tenant_id = 10
       AND status = 'published'
       AND search_text @@ websearch_to_tsquery('simple', :'q')
     ORDER BY ts_rank(search_text, websearch_to_tsquery('simple', :'q')) DESC
     LIMIT 20;"
done

DATABASE_URL 指向测试数据库即可运行。使用 TIN 时,保持查询集合和数据快照不变,记录索引构建时间、查询计划、缓冲区访问和结果数量,比较才有意义。

写入与维护成本

全文索引不是免费的。文档更新会触发索引维护,批量导入会增加构建时间和磁盘占用。因此需要观察:

  • 单条插入和更新的吞吐量;
  • 批量导入期间的数据库负载;
  • 索引大小与表大小的比例;
  • 删除大量文档后空间是否需要回收或重建;
  • 备份、恢复和升级流程是否包含该索引。

TIN 的“快速”应放在具体工作负载中验证:不同语言、文档长度、更新比例和过滤条件,可能产生完全不同的结果。

相关性与边界条件

速度只是搜索体验的一部分。还要用一组人工标注的查询检查结果质量,例如:

  • 标题命中是否应该高于正文命中;
  • 多词查询是否需要同时命中所有词;
  • 结果排序是否会被更新时间压过相关性;
  • 不同租户之间是否绝对不会串数据;
  • 被撤回或无权访问的文档是否仍可能出现在结果中。

特别是权限过滤,不要只在应用层过滤搜索结果。正确的做法是让租户、状态和可见性条件进入数据库查询,并为这些条件设计相应的测试。

什么时候适合采用,什么时候保持分离

TIN 更适合以下类型的项目:

  • 已经以 PostgreSQL 作为核心数据存储;
  • 搜索结果必须和事务数据保持紧密一致;
  • 希望减少同步链路和运维组件;
  • 搜索主要是站内文档、知识库、工单或业务记录;
  • 团队愿意用 SQL 和数据库工具完成搜索能力建设。

仍然应该谨慎评估以下情况:

  • 需要跨多个数据源建立统一搜索索引;
  • 依赖复杂的搜索分析、推荐、拼写纠错或联想能力;
  • 数据规模、写入峰值或隔离需求已经超过单个 PostgreSQL 集群的舒适范围;
  • 团队已经有成熟的外部搜索平台和完善的数据同步体系。

这不是“数据库搜索一定优于专用搜索引擎”的结论。TIN 的吸引力在于减少系统边界,同时提供一个面向全文检索的索引选项;是否值得采用,最终要由真实数据和真实查询决定。

一份可执行的落地清单

  1. 用固定数据集定义查询、过滤、排序和权限契约。
  2. 先建立当前实现的基线,记录 p50、p95、p99、索引大小和写入吞吐。
  3. 按 TIN 的官方安装与索引配置方式建立隔离测试环境,不要直接在生产库试验。
  4. 使用同一批查询比较延迟、结果质量和更新可见性。
  5. 验证备份恢复、升级、故障切换和索引重建流程。
  6. 上线时保留回退方案,并监控查询长尾、数据库 CPU、内存、磁盘和锁等待。

如果你的系统已经被“数据库加同步搜索集群”的复杂度拖慢,TIN 值得作为一个严肃的候选方案进行基准测试。最稳妥的路径不是凭产品描述做决定,而是把全文搜索变成一组可重复、可观测、可回退的数据库实验。


相关推荐