在长文本字段上使用 PostgreSQL trigrams 进行缓慢的模糊搜索

问题描述 投票:0回答:1

当我在 PostgreSQL 中对相对较小的记录集 (8k) 执行模糊文本搜索时,我发现查询速度很慢(约 20 秒)。我已经设置了 GIN 和 GIST 三元组索引,并通过 ANALYZE 确认它们正在被使用。性能似乎是由少量具有很长文本字段的记录驱动的。虽然大多数记录都在 2000 个字符以下,但大约有 200 个超过 10k,还有一些超过 20k。将这些长字符串截断为 10k 个字符,可将查询时间缩短至 8 秒。

我尝试过 PostgreSQL 10 和 11、GIN 和 GIST 索引,性能没有实质性变化。

我尝试将文本内容分割成更多的记录,每个记录要小得多。搜索分布在更多记录中的相同文本会导致亚秒级查询。

这个查询完全不起眼:

select title, 
'orthopedic surgeon' <<-> content as sml
from answers
where 'orthopedic surgeon' <% content
order by sml desc

对于如此少量的记录,即使我的文本字段有超过 30k 个字符,我也希望三元组索引能够提供近乎即时的结果。我不希望通过将内容拆分到更多记录来实现更快的查询。这样的表现正常吗?

database postgresql performance full-text-search
1个回答
0
投票

显然,很晚才谈到这个问题,但我正在为类似的问题而苦苦挣扎,而你对拆分文本获得更好性能的观察使我点击了。

三元组索引的工作原理是在目标文档中查找搜索字符串的位。这部分非常快,基本上是 O(1) 时间。因此,如果您正在搜索“gold star”,它将立即找到包含每个三元组“gol”、“old”、“ld”、“d s”、“ st”、“sta”和“焦油”。

但是,不能保证卦象的顺序正确。也许您有一条记录,其中包含文本“old star is gold”——它包含索引正在查找的所有内容,但实际上在任何地方都不包含“gold star”。因此,在确定了一组候选记录后,Postgres 现在必须扫描每个候选记录,以验证所需的文本是否确实存在于所需的序列中。这部分只是一个普通的旧文本扫描,以 O(N) 的速度艰难地浏览整个记录,直到找到文本(或找不到)。

因此,您希望这个过程的第一步——索引部分——完成尽可能多的工作。如果您有一个 10,000 个字符的记录,其中您的搜索字符串出现了两次,并且您没有将其拆分,那么 Postgres 在确认子字符串存在之前将必须扫描(平均)3,333 个字符。如果将其拆分为一百个 100 个字符的子记录,索引将剔除其中的 98 个,而 Postgres 平均只需扫描 100 个字符,从而将操作速度提高了 33 倍。

(可能不止于此——我怀疑 Postgres 中有一些优化只适用于特定的文本长度——但这肯定是其中的一部分。)

© www.soinside.com 2019 - 2024. All rights reserved.