全文索引与并行查询
全文索引
介绍
PostgreSQL 内置了全文检索功能,但默认仅支持英文检索。
可通过配置插件(如 zhparser)实现对中文的全文检索。
全文检索初步使用
PostgreSQL 将长文本分解为许多 token 的集合,称为 tsvector,代表文档内容。检索实际上是在 token 集合中进行的。
示例
sql
-- 将文本转为 tsvector
SELECT 'we love postgresql database'::tsvector;
-- 使用函数分词
SELECT to_tsvector('we love postgresql database');
检索条件类型为 tsquery,是由简单逻辑运算符组成的字符串。
sql
SELECT 'postgresql & love'::tsquery;
SELECT to_tsquery('postgresql & love');
全文检索使用 tsvector 和 tsquery 进行匹配,运算符为 @@:
sql
SELECT 'we love postgresql database'::tsvector @@ 'postgresql & love'::tsquery; -- true
SELECT 'we love postgresql database'::tsvector @@ 'mysql | love'::tsquery; -- true
SELECT 'we love postgresql database'::tsvector @@ 'mysql & love'::tsquery; -- false
使用全文检索实际案例
建表并插入数据
sql
CREATE TABLE myblog (
id INT PRIMARY KEY,
content TEXT,
content_tsv TSVECTOR
);
-- 插入 100 万条记录
INSERT INTO myblog(id, content)
SELECT seq, 'PostgresQL' || seq || ' MySQL' || (seq + 10)
FROM generate_series(1, 1000000) AS seq;
-- 分词处理
UPDATE myblog SET content_tsv = to_tsvector(content);
查询示例
sql
-- 查询包含 "postgresql1323" 的记录
SELECT * FROM myblog WHERE content_tsv @@ 'postgresql1323'::tsquery;
EXPLAIN SELECT * FROM myblog WHERE content_tsv @@ 'postgresql1323'::tsquery;
此时会进行全表扫描,性能较差。
创建 GIN 索引加速
sql
CREATE INDEX idx_myblog_content_tsv ON myblog USING GIN(content_tsv);
-- 再次查询
SELECT * FROM myblog WHERE content_tsv @@ 'postgresql1323'::tsquery;
EXPLAIN SELECT * FROM myblog WHERE content_tsv @@ 'postgresql1323'::tsquery;
使用 zhparser 做中文全文检索
安装依赖
-
安装 PostgreSQL 开发包(以 PostgreSQL 15 为例):
bashyum install postgresql15-devel -
安装 scws(中文分词器):
bashwget -q http://www.xunsearch.com/scws/down/scws-1.2.3.tar.bz2 tar xvf scws-1.2.3.tar.bz2 cd scws-1.2.3 ./configure make make install -
安装 zhparser 插件(需从 GitHub 下载编译)。
配置与使用
sql
-- 创建扩展
CREATE EXTENSION zhparser;
-- 创建中文全文检索配置
CREATE TEXT SEARCH CONFIGURATION testzhcfg (PARSER = zhparser);
ALTER TEXT SEARCH CONFIGURATION testzhcfg
ADD MAPPING FOR n,v,a,i,e,l WITH simple;
n, v, a, i, e, l表示不同的 Token 策略,启用了这些 Token Mapping。- 使用
\dfp+ zhparser查看详细含义。
示例表与数据
sql
CREATE TABLE myblog (
id INT PRIMARY KEY,
content TEXT,
content_tsv TSVECTOR
);
INSERT INTO myblog(id, content) VALUES
(1, '中国南京市长江大桥'),
(2, '是一种特性非常齐全的自由软件的对象-关系型数据库管理系统'),
(3, '是以加州大学计算机系开发的POSTGRES'),
(4, '4.2版本为基础的对象关系型数据库管理系统'),
(5, 'PostgreSQL是一个功能非常强大的、源代码开放的客户/服务器关系型数据库管理系统(RDBMS)'),
(6, 'PostgreSQL是一个非常健壮的软件包,有很多在大型商业RDBMS中所具有的特性'),
(7, '包括事务、子选择、触发器、视图、外键引用完整性和复杂锁定功能'),
(8, '全球开发组今天宣布,世界上功能最为强大的开源数据库发布');
-- 使用 testzhcfg 配置分词
UPDATE myblog SET content_tsv = to_tsvector('testzhcfg', content);
-- 创建 GIN 索引
CREATE INDEX idx_myblog_content_tsv ON myblog USING GIN(content_tsv);
中文全文检索查询
sql
-- 检索包含"南京市"的文档
SELECT * FROM myblog WHERE content_tsv @@ to_tsquery('testzhcfg', '南京市');
-- 中文检索中也可查询英文单词
SELECT id, content FROM myblog WHERE content_tsv @@ to_tsquery('testzhcfg', 'postgresql');
并行查询功能
并行查询利用多 CPU 和多核的能力,可显著缩短大数据量查询的处理时间。
并行查询相关配置参数
| 参数名 | 说明 |
|---|---|
dynamic_shared_memory_type |
必须设置为非 none 值,用于进程间数据传递 |
max_worker_processes |
整个数据库实例允许的最大后台工作进程数(默认 8) |
max_parallel_workers |
整个实例允许用于并行的后台工作进程数 |
max_parallel_workers_per_gather |
单个并行操作允许的并行度(需小于 max_parallel_workers) |
min_parallel_table_scan_size |
表大小小于此值时不走并行(默认 8MB) |
min_parallel_index_scan_size |
索引扫描大小小于此值时不走并行(默认 512KB) |
成本估算参数
parallel_setup_cost:并行启动成本parallel_tuple_cost:并行处理每行数据的成本
是否使用并行取决于 CBO(成本优化器)的计算结果。若非并行的成本低于并行成本,则不使用并行。
强制并行模式
sql
SET force_parallel_mode = on; -- 慎用于生产环境,一般用于测试
支持的并行操作
| 操作类型 | 说明 |
|---|---|
| 并行顺序扫描 | 全表扫描(sequential scan) |
| 并行索引扫描 | 索引扫描(index scan) |
| 并行 index-only 扫描 | index only 扫描 |
| 并行 bitmap heap 扫描 | bitmap heap 扫描 |
| 并行聚合 | COUNT()、SUM() 等聚合操作 |
| Nested loop 并行 | Nested loop 多表关联 |
| Merge join 并行 | Merge join 多表关联 |
| Hash Join 并行 | Hash Join 多表关联 |
| 并行 create index | BTree 索引的并行创建 |
CREATE TABLE ... AS |
支持并行执行 |
总结
| 功能 | 关键点 |
|---|---|
| 全文索引 | 内置英文检索,通过插件支持中文;使用 tsvector/tsquery 和 GIN 索引 |
| zhparser | 需安装 scws 分词器,配置 Token Mapping,支持中英文混合检索 |
| 并行查询 | 利用多核 CPU,需配置相关参数;CBO 决定是否并行;支持多种扫描与连接操作 |