当您需要在海量文本中进行全文检索,并且希望对检索结果基于相关度或时间戳等字段进行高性能排序时,RUM插件是比PostgreSQL内置GIN索引更优的选择。传统的GIN索引在排序时需要回表(二次访问数据表),导致性能瓶颈。RUM插件通过在索引中预存排序所需的位置和附加信息,避免了回表操作,可将特定场景下的查询性能提升数倍。
适用范围
支持的PolarDB PostgreSQL版的版本如下:
PostgreSQL 18(内核小版本2.0.18.1.2.0及以上)
PostgreSQL 17(内核小版本2.0.17.6.4.0及以上)
PostgreSQL 16(内核小版本2.0.16.8.3.0及以上)
PostgreSQL 15(内核小版本2.0.15.7.1.1及以上)
PostgreSQL 14(内核小版本2.0.14.5.3.0及以上)
插件兼容性:RUM插件的
%操作符与smlar插件的%操作符存在冲突。因此,这两个插件不能在同一个数据库模式(Schema)下同时创建和使用。请在安装前确认您的环境中没有使用smlar插件,或将它们安装在不同的Schema中。
适用场景
在决定是否使用RUM插件前,请参考下表了解其与原生GIN索引的核心差异,以便做出最适合您业务场景的技术选型。
对比维度 | GIN索引(内置) | RUM索引 | 决策建议 |
核心优势 | 写入性能好,索引体积相对较小。 | 排序性能高,支持短语搜索和附加列排序。 | 需要对检索结果排序的场景,请优先选择RUM。 |
排序性能 | 慢。需要回表获取排序依据,性能随数据量增大而急剧下降。 | 快。直接在索引内完成排序,无需回表。 | 对全文检索结果有高频排序需求(如按相关性、时间、价格),推荐使用RUM。 |
短语搜索 | 慢。需要回表获取词汇位置信息以验证短语。 | 快。词汇位置信息已在索引中,无需回表。 | 对短语搜索性能有要求的场景,推荐使用RUM。 |
附加列排序 | 不支持。无法在索引中存储额外列(如时间戳)的信息。 | 支持。可在索引中附加其他列,实现高性能自定义排序。 | 需要按文章发布时间、更新时间等排序的场景,RUM优势大。 |
写入性能 | 较快。 | 较慢。需要维护更复杂的索引结构。 | 在写入密集型(高频 |
索引体积 | 较小。 | 较大。需要额外空间存储位置和附加信息。 | 评估您的存储成本,如果索引体积是主要瓶颈,可考虑RUM的 |
前缀搜索 | 支持。 |
| 如果前缀搜索是核心需求,避免使用RUM的 |
用RUM以空间换时间,通过增加索引体积和写入开销,有效提升了特定查询场景(尤其是排序)的性能。如果您的业务核心是带有复杂排序的全文检索,RUM是理想选择。如果您的业务主要是写入密集型或仅需简单文本匹配,GIN则更具成本效益。
注意事项
写入性能与索引体积:RUM索引为了加速查询,需要存储额外信息(如词汇位置),这导致其索引体积通常大于GIN索引,并且在数据写入和更新时,索引构建的开销也更高。因此,在写入密集型且对存储空间敏感的场景下,需谨慎评估其成本。
不支持前缀搜索的场景:使用
rum_tsvector_hash_ops或rum_tsvector_hash_addon_ops操作符类创建的索引,由于存储的是词素的哈希值而非原文,因此不支持前缀搜索。
安装与卸载插件
安装插件
在开始使用前,您需要在数据库中执行以下命令来创建RUM插件。
CREATE EXTENSION rum;卸载插件
如果您不再需要RUM插件,可以执行以下命令进行卸载。
DROP EXTENSION rum;使用方法
RUM插件提供了多种操作符类(Operator Class),以支持不同的数据类型和查询场景。您可以根据具体需求选择合适的操作符类来创建索引。
操作符类(Operator Class)明确了RUM索引在处理特定数据类型时所需使用的一组操作,从而让索引能够正确地存储和检索该类型的数据。每个操作符类(Operator Class)都包含一组特定的操作符。当你WHERE子句或ORDER BY子句中使用这些受支持的操作符时,PostgreSQL就能利用RUM索引来有效地加速查询。
因此,选择正确的操作符类(Operator Class)是让RUM索引生效的关键。更多信息,请参见Operator Classes and Operator Families。
操作符
操作符 | 支持类型 | 返回值类型 | 描述 |
A | 左 |
| 返回全文向量是否匹配查询条件,会进行距离计算。 |
A | 左 |
| 返回全文向量和查询条件的距离值,值越小相关性越高。 |
|
| 返回两个值之间的绝对差值。
| |
A |
|
| 仅当 |
A |
|
| 仅当 |
Operator Class(操作符类)
操作符类 | 适用数据类型 | 支持的关键操作符 | 核心功能与说明 |
|
|
| 存储 |
|
|
| 存储
|
|
|
| 对非文本、非数组类型的数据进行范围查询和距离排序。 |
|
|
| 在 说明 附加列的数据类型需有对应的 |
|
|
| 功能同
|
|
|
| 用于索引 |
|
|
| 索引数组类型,支持包含、重叠等数组操作,并支持按数组间距离排序。 |
|
|
| 在数组索引的基础上,附加一个额外列的数据,以支持更复杂的查询场景。 说明 附加列的数据类型需有对应的 |
场景一:加速全文检索结果的相关性排序
当您需要对全文检索结果按相关性排序时,使用RUM索引可以避免GIN索引所需的额外排序开销,实现高性能排序。
准备数据:首先,创建一张测试表。
CREATE TABLE t1( t text, t_vec tsvector GENERATED ALWAYS AS (to_tsvector('pg_catalog.english', t)) STORED ); -- 插入测试数据 INSERT INTO t1(t) VALUES ('The situation is most beautiful'); INSERT INTO t1(t) VALUES ('It is a beautiful'); INSERT INTO t1(t) VALUES ('It looks like a beautiful place');创建RUM索引:使用
rum_tsvector_ops操作符类为tsvector列创建RUM索引。CREATE INDEX t1_t_vec_idx ON t1 USING rum (t_vec rum_tsvector_ops);执行相关性排序查询:使用
<=>操作符进行查询。此操作符计算查询与文本的距离,距离值越小,代表相关性越高。因此,通过ORDER BY即可实现按相关性降序排列。SET enable_seqscan TO off; SELECT t, t_vec <=> to_tsquery('english', 'beautiful | place') AS rank FROM t1 WHERE t_vec @@ to_tsquery('english', 'beautiful | place') ORDER BY t_vec <=> to_tsquery('english', 'beautiful | place');返回结果如下:
t | rank ---------------------------------+--------- It looks like a beautiful place | 8.22467 The situation is most beautiful | 16.4493 It is a beautiful | 16.4493
场景二:加速全文检索与附加列的联合排序
在日志分析、电商搜索等场景中,经常需要在全文检索的同时,根据时间戳或价格等附加字段进行排序。RUM通过addon功能,可以将附加列的信息存入索引,实现高效的联合查询和排序。
准备数据:创建一张包含
tsvector列和时间戳列的表,并插入示例数据。CREATE TABLE tsts (id int, t tsvector, d timestamp); INSERT INTO tsts VALUES (354, to_tsvector('wr qh'), '2016-05-16 14:21:22.326724'), (355, to_tsvector('wr qh'), '2016-05-16 13:21:22.326724'), (356, to_tsvector('ts op'), '2016-05-16 18:21:22.326724'), (358, to_tsvector('ts op'), '2016-05-16 23:21:22.326724'), (371, to_tsvector('wr qh'), '2016-05-17 06:21:22.326724'), (406, to_tsvector('wr qh'), '2016-05-18 17:21:22.326724'), (415, to_tsvector('wr qh'), '2016-05-19 02:21:22.326724');创建带附加列的RUM索引:使用
rum_tsvector_addon_ops操作符类,并通过WITH子句指定附加列和主索引列。CREATE INDEX tsts_idx ON tsts USING rum (t rum_tsvector_addon_ops, d) WITH (attach = 'd', to = 't');说明关键语法
WITH (attach = 'd', to = 't')的作用是将d列(附加列,此处为时间戳)的值附加到t列(主索引列,tsvector类型)的索引条目中。这使得数据库在一次索引扫描中,就能同时利用t列的索引进行全文检索,并利用附加的d列信息进行高效排序,避免了回表查询,从而有效提升性能。执行联合排序查询:查询包含特定词汇的记录,并按时间戳与目标时间的接近程度排序。
SET enable_seqscan TO off; EXPLAIN (costs off) SELECT id, d, d <=> '2016-05-16 14:21:25' AS distance FROM tsts WHERE t @@ 'wr&qh' ORDER BY d <=> '2016-05-16 14:21:25' LIMIT 5;执行计划如下,排序和过滤均在一次索引扫描中完成。
QUERY PLAN ------------------------------------------------------------------------------ Limit -> Index Scan using tsts_idx on tsts Index Cond: (t @@ '''wr'' & ''qh'''::tsquery) Order By: (d <=> '2016-05-16 14:21:25'::timestamp without time zone)查询结果如下:
id | d | distance -----+----------------------------+--------------- 354 | 2016-05-16 14:21:22.326724 | 2.673276 355 | 2016-05-16 13:21:22.326724 | 3602.673276 371 | 2016-05-17 06:21:22.326724 | 57597.326724 406 | 2016-05-18 17:21:22.326724 | 183597.326724 415 | 2016-05-19 02:21:22.326724 | 215997.326724
场景三:加速数组查询与相似度排序
对于标签系统或用户画像等场景,需要高效查询包含特定元素的数组,并按数组元素的重合度或相似度进行排序。
准备数据:
CREATE TABLE test_array (id serial, i int2[]); INSERT INTO test_array(i) VALUES ('{}'), ('{0}'), ('{1,2,3,4}'), ('{1,2,3}'), ('{1,2}'), ('{1}');创建数组RUM索引:使用
rum_anyarray_ops操作符类为数组类型的列创建索引。CREATE INDEX idx_array ON test_array USING rum (i rum_anyarray_ops);执行数组查询与排序:查询包含元素
1的记录,并按与{1}的相似度排序。SELECT * FROM test_array WHERE i && '{1}' -- '&&' 操作符表示数组重叠 ORDER BY i <=> '{1}' ASC; -- '<=>' 操作符计算数组间的距离,值越小越相似返回结果如下:
i ----------- {1} {1,2} {1,2,3} {1,2,3,4}
场景四:反向索引,快速匹配查询规则
在构建用户订阅、告警规则匹配等系统时,需要用一条新数据(如一篇文章)去快速匹配海量的存量查询规则(如用户的订阅关键词)。RUM支持对tsquery类型创建索引,实现高效的反向匹配。
准备查询规则数据:
CREATE TABLE query (id serial, q tsquery, tag text); INSERT INTO query (q, tag) VALUES ('supernova & star', 'sn'), ('black', 'color'), ('big & bang & black & hole', 'bang'), ('spiral & galaxy', 'shape'), ('black & hole', 'color');创建
tsquery的RUM索引:CREATE INDEX query_idx ON query USING rum(q rum_tsquery_ops);执行反向匹配查询:使用一篇新文章的
tsvector来匹配所有符合条件的tsquery规则。SELECT * FROM query WHERE to_tsvector('black holes never exists before we think about them') @@ q;返回结果如下:
id | q | tag -----+----------+------- 2 | 'black' | color