JSONB、全文与语义检索
选择关系列、JSONB、全文检索与 pgvector 的边界
PostgreSQL 可以在同一系统里处理关系数据、JSON 文档、词法全文检索,并通过扩展进行向量检索。能放在一起不代表应该把所有问题都塞进一列。
何时使用 JSONB
适合:来源不统一的元数据、低频变化的可选属性、需要保留原始载荷的集成数据。
不适合:主键与外键、金额、权限边界、经常 JOIN/排序/聚合的核心字段。
CREATE TABLE products (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
sku text NOT NULL UNIQUE,
name text NOT NULL,
attributes jsonb NOT NULL DEFAULT '{}'::jsonb,
CHECK (jsonb_typeof(attributes) = 'object')
);
INSERT INTO products (sku, name, attributes)
VALUES ('KB-01', 'Keyboard', '{"layout":"75%","wireless":true}');
SELECT id, name
FROM products
WHERE attributes @> '{"wireless":true}';JSONB 索引
CREATE INDEX products_attributes_gin
ON products USING gin (attributes);默认 GIN operator class 支持多种键与包含查询。若工作负载几乎只有 @>,jsonb_path_ops 通常索引更小,但支持的运算符集合更窄。用真实查询和数据分布比较。
频繁查询的单个属性可以使用表达式索引,或提升为普通列:
CREATE INDEX products_layout_idx
ON products ((attributes ->> 'layout'));内置全文检索
ALTER TABLE products ADD COLUMN search_document tsvector
GENERATED ALWAYS AS (
setweight(to_tsvector('simple', coalesce(name, '')), 'A') ||
setweight(to_tsvector('simple', coalesce(attributes::text, '')), 'B')
) STORED;
CREATE INDEX products_search_gin
ON products USING gin (search_document);
SELECT id, name,
ts_rank(search_document, websearch_to_tsquery('simple', $1)) AS rank
FROM products
WHERE search_document @@ websearch_to_tsquery('simple', $1)
ORDER BY rank DESC
LIMIT 20;中文分词不由内置 simple 配置完整解决;生产中文搜索需要评估专用分词扩展、应用侧分词或外部搜索系统。
语义检索与 pgvector
向量不是 PostgreSQL 核心内置类型。常见方案是安装独立的 pgvector 扩展:
CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE document_chunks (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
document_id bigint NOT NULL,
content text NOT NULL,
embedding vector(1536) NOT NULL,
embedding_model text NOT NULL
);维度必须匹配模型;切换 embedding 模型时不要在同一索引里混用不可比较的向量。保存模型名、切块版本和源文档定位,才能重建与审计。
混合检索通常更稳
用关键词/权限/时间等结构化条件缩小候选集,再做向量相似度排序。始终在 SQL 中执行租户和访问控制过滤,不要依赖模型自行遵守。
需要可运行环境时先阅读安装 pgvector;准备建立近似索引前阅读pgvector 生产最佳实践。
Last updated on