在Postgres 9.1数据库中,我有一个table1
约150万行和一列的表label
(为方便起见,使用简化名称)。
上有一个功能性trigram-index lower(unaccent(label))
(unaccent()
已使其不可变,以允许在索引中使用)。
以下查询非常快:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword%')));
count
-------
1
(1 row)
Time: 394,295 ms
但是以下查询速度较慢:
SELECT count(*) FROM table1
WHERE (lower(unaccent(label)) like lower(unaccent('%someword and some more%')));
count
-------
1
(1 row)
Time: 1405,749 ms
即使搜索更加严格,添加更多单词的速度甚至会更慢。
我尝试了一个简单的技巧,即先对第一个单词运行子查询,然后对完整的搜索字符串进行查询,但是(不幸的是)查询计划者看到了我的想法:
EXPLAIN ANALYZE
SELECT * FROM (
SELECT id, title, label from table1
WHERE lower(unaccent(label)) like lower(unaccent('%someword%'))
) t1
WHERE lower(unaccent(label)) like lower(unaccent('%someword and some more%'));
对table1进行位图堆扫描(成本= 16216.01..16220.04行= 1宽度= 212)(实际时间= 1824.017..1824.019行= 1循环= 1) 重新检查条件:((lower(unaccent((label):: text))~~'%someword%':: text)AND(lower(unaccent((label):: text))~~'%someword等%'::文本)) ->对table1_label_hun_gin_trgm进行位图索引扫描(成本= 0.00..16216.01行= 1宽度= 0)(实际时间= 1823.900..1823.900行= 1循环= 1) 索引条件:((lower(unaccent((label):: text))~~'%someword%':: text)AND(lower(unaccent((label):: text))~~'%someword等%'::文本)) 总运行时间:1824.064毫秒
我的最终问题是搜索字符串来自Web界面,该Web界面可能会发送很长的字符串,因此发送速度很慢,并且还可能构成DOS向量。
所以我的问题是:
- 如何加快查询速度?
- 有没有一种方法可以将其分为子查询,以便更快?
- 也许Postgres的更高版本更好?(我尝试了9.4,它似乎并不快:效果还是一样。也许是更高版本?)
- 也许需要不同的索引策略?
unaccent
不可变。我将此添加到问题中。
unaccent
模块时,hack将被覆盖。我建议使用函数包装的原因之一。
unaccent()
它也是由附加模块提供的,并且Postgres 默认情况下不支持该函数上的索引,因为它不支持IMMUTABLE
。您必须进行了一些更改,并且应该提及您在问题中所做的工作。我的常规建议:stackoverflow.com/a/11007216/939860。另外,trigram索引支持开箱即用的不区分大小写的匹配。您可以简化为:WHERE f_unaccent(label) ILIKE f_unaccent('%someword%')
-具有匹配的索引。详细信息:stackoverflow.com/a/28636000/939860。