我在表
character varying
中有一个名为 message
的 messages
列,用于存储来自 IRC 频道的用户消息。通过聊天机器人,我允许用户搜索某个术语已被输入的次数。该术语可以是任何内容:一个字符、一个单词或多个单词。该表有大约 1500 万行,查询时间可能相当长。
我使用以下查询来查找所有匹配
term
的子字符串(不区分大小写):
select sum(array_length(string_to_array(LOWER(message), LOWER('term')), 1) -1) from messages;
查询采用顺序扫描。当我
set enable_seqscan = off;
时,它使用我在桌子上也有的 btree
索引。该表还有一个三元组索引,但它从未被使用过。
您会从什么角度来提高查询性能?
我使用 Postgres 14.9
您可以为此应用程序使用 postgreSQL 优化吗?是的,但不是你组织的方式。
首先计算
messages
行,其中 message
列包含任意用户提供的搜索词。您可以通过此查询来执行此操作。
SELECT COUNT(*) FROM messages WHERE message ILIKE '%term%'
然后,在您要搜索的列上创建一个所谓的 trigram 索引。你会这样做的。
CREATE EXTENSION IF NOT EXISTS pg_trgm;
CREATE INDEX CONCURRENTLY message_text ON messages
USING GIN (message gin_trgm_ops);
这种索引设置是 postgreSQL 独有的,可加速
LIKE
和 ILIKE
谓词。
此技术返回包含“一个或多个”搜索词出现的消息数,而您的要求要求返回搜索词出现的“总数”。要获取总数,您可以使用子查询仅过滤出现该术语的消息,然后对这些消息进行计数。这比搜索所有消息要快。
select sum(array_length(string_to_array(LOWER(message), LOWER('term')), 1) -1)
from (
SELECT message FROM messages WHERE message ILIKE '%term%'
) subset
如果这是我的应用程序,在将其投入生产之前,我将禁止搜索短于三个或四个字母的术语,我什至可能创建一个不允许的停用词表。这是因为这些查询在返回大量计数时会减慢速度。有人可能会使用像
'e'
这样的简短搜索词来拒绝向您的用户提供服务。