概述
PostgreSQL 内置了全文搜索引擎,用 tsvector 和 tsquery 就能做关键词搜索、相关度排序。但默认配置只支持英文分词,中文文本直接搜基本没用。
要支持中文,需要装 zhparser 或 pg_jieba 这类中文分词插件。zhparser 基于 HDWK 分词算法,性能不错,是社区最常用的选择。
本文用 Docker 跑 PG 容器,顺手把 zhparser 编译安装好,配置完就能搜中文了。
准备工作
- 一台 VPS(1 核 CPU、1 GB 内存起步,中文分词不算吃资源)
- 已安装 Docker 和 Docker Compose
- 有 sudo 权限
- 磁盘 5 GB 以上
步骤
第一步:创建项目目录
mkdir -p ~/pg-fts && cd ~/pg-fts
第二步:写 docker-compose.yml
version: '3.8'
services:
postgres:
image: postgres:16
container_name: pg-fts
restart: unless-stopped
environment:
POSTGRES_USER: ftsuser
POSTGRES_PASSWORD: ftspass2024!
POSTGRES_DB: ftsdb
volumes:
- pgfts_data:/var/lib/postgresql/data
- ./init-db.sh:/docker-entrypoint-initdb.d/init-db.sh
ports:
- "5432:5432"
volumes:
pgfts_data:
注意:把 POSTGRES_PASSWORD 改成你自己的密码。
第三步:写初始化脚本
在项目目录下新建 init-db.sh:
#!/bin/bash
set -e
# 安装 zhparser 扩展
psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
CREATE EXTENSION IF NOT EXISTS zhparser;
DROP TEXT SEARCH CONFIGURATION zhparser;
CREATE TEXT SEARCH CONFIGURATION zhparser ( PARSER = zhparser );
ADD MAPPING FOR n,v,a,i,e,l WITH simple;
EOSQL
保存后给执行权限:
chmod +x init-db.sh
第四步:启动服务
docker-compose up -d
启动后检查容器状态:
docker-compose ps
应该显示 Up 状态。如果启动失败,用 docker-compose logs 查看报错。
第五步:验证 zhparser 已生效
docker-compose exec postgres psql -U ftsuser -d ftsdb -c "SELECT * FROM pg_ts_parser LIMIT 1;"
输出里应该有 zhparser 一行。再确认配置:
docker-compose exec postgres psql -U ftsuser -d ftsdb -c "SELECT cfgname FROM pg_ts_config WHERE cfgname = 'zhparser';"
应该输出 zhparser。
第六步:建表并测试搜索
docker-compose exec postgres psql -U ftsuser -d ftsdb <<'SQL'
CREATE TABLE articles (
id SERIAL PRIMARY KEY,
title TEXT,
content TEXT,
search_vector TSVECTOR
);
-- 创建触发器,自动维护 tsvector
CREATE OR REPLACE FUNCTION update_search_vector() RETURNS TRIGGER AS \$\$
BEGIN
NEW.search_vector :=
to_tsvector('zhparser', COALESCE(NEW.title, '') || ' ' || COALESCE(NEW.content, ''));
RETURN NEW;
END;
\$\$ LANGUAGE plpgsql;
CREATE TRIGGER tsvector_update BEFORE INSERT OR UPDATE ON articles
FOR EACH ROW EXECUTE FUNCTION update_search_vector();
-- 插入测试数据
INSERT INTO articles (title, content) VALUES
('Docker 容器入门教程', 'Docker 是一个开源的应用容器引擎,开发者可以打包应用到容器里'),
('PostgreSQL 全文搜索指南', 'PostgreSQL 提供了强大的全文搜索功能,支持中文分词'),
('Kubernetes 部署实践', 'Kubernetes 是容器编排平台,可以帮助管理大规模 Docker 容器集群'),
('Python 数据处理技巧', 'Python 是数据科学主流语言,pandas 和 numpy 是常用工具包');
-- 建索引加速搜索
CREATE INDEX idx_articles_vector ON articles USING GIN(search_vector);
-- 测试搜索
SELECT id, title, ts_rank(search_vector, query) AS rank
FROM articles, to_tsquery('zhparser', 'Docker OR PostgreSQL') AS query
ORDER BY rank DESC;
SQL
输出应该显示标题和搜索相关度排名,“Docker” 和 “PostgreSQL” 两篇排在最前面。
验证
确认搜索功能正常
docker-compose exec postgres psql -U ftsuser -d ftsdb <<'SQL'
-- 搜"容器",应该看到 Docker 和 Kubernetes 两篇
SELECT id, title, to_tsvector('zhparser', title) AS parsed
FROM articles
WHERE search_vector @@ to_tsquery('zhparser', '容器');
SQL
确认索引生效
docker-compose exec postgres psql -U ftsuser -d ftsdb -c "SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'articles';"
应该看到 idx_articles_vector 使用 GIN 索引。
常见问题
Q: zhparser 编译时报"Makefile: no such file"
PostgreSQL 官方镜像不内置 zhparser,需要通过 init-db.sh 自动从源码编译。如果容器里网络不通,先确保 Docker 能访问外网:
docker-compose exec postgres apt-get update && apt-get install -y make gcc libpq-dev
Q: 搜中文返回空结果?
先检查分词结果:
docker-compose exec postgres psql -U ftsuser -d ftsdb -c "SELECT to_tsvector('zhparser', 'Docker 容器入门教程');"
如果输出是空 {},说明分词器没装好,检查 init-db.sh 是否正确执行。
Q: 中文分词效果不理想,想换 jieba?
把 init-db.sh 里的 zhparser 换成 pg_jieba:
# 安装 jieba
docker-compose exec postgres apt-get install -y python3-pip
pip3 install jieba
# 创建扩展
psql -c "CREATE EXTENSION pg_jieba;"
psql -c "SELECT * FROM pg_ts_parser LIMIT 1;"
Q: 如何远程连接?
PostgreSQL 默认只监听 localhost。容器内修改配置:
docker-compose exec postgres sh -c "echo \"listen_addresses = '*'\" >> /var/lib/postgresql/data/postgresql.conf"
docker-compose restart postgres
同时在 pg_hba.conf 里加允许远程的规则:
docker-compose exec postgres sh -c "echo \"host all all 0.0.0.0/0 md5\" >> /var/lib/postgresql/data/pg_hba.conf"
docker-compose restart postgres
下一步
搜到数据只是第一步。生产环境建议加上备份、监控和 SSL 加密。
数据量大了之后,GIN 索引维护成本会上升,可以考虑用 pg_trgm 做模糊匹配补充。
装好了,下一篇可以聊聊如何用 Elasticsearch 做更复杂的搜索场景。