如何创建索引以加快对表达式的聚合LIKE查询?


20

我可能在标题中提出了错误的问题。这是事实:

我的客户服务人员一直抱怨在基于Django的站点的管理界面上进行客户查找时响应速度慢。

我们正在使用Postgres 8.4.6。我开始记录慢速查询,并发现了这个罪魁祸首:

SELECT COUNT(*) FROM "auth_user" WHERE UPPER("auth_user"."email"::text) LIKE UPPER(E'%deyk%')

此查询最多需要32秒才能运行。这是EXPLAIN提供的查询计划:

QUERY PLAN
Aggregate  (cost=205171.71..205171.72 rows=1 width=0)
  ->  Seq Scan on auth_user  (cost=0.00..205166.46 rows=2096 width=0)
        Filter: (upper((email)::text) ~~ '%DEYK%'::text)

因为这是由Django ORM从Django Admin应用程序生成的Django QuerySet中生成的查询,所以我对该查询本身没有任何控制权。索引似乎是合理的解决方案。我尝试创建索引来加快速度,但是并没有什么不同:

CREATE INDEX auth_user_email_upper ON auth_user USING btree (upper(email::text))

我究竟做错了什么?如何加快查询速度?

Answers:


21

PostgreSQL 8.4中,没有对LIKE/的索引支持- 除左锚定搜索词外ILIKE

PostgreSQL 9.1开始,附加模块pg_trgm为GIN和GiST三字母索引提供了运算符类,以支持LIKE/ ILIKE或正则表达式(运算符~和朋友)。每个数据库安装一次:

CREATE EXTENSION pg_trgm;

示例GIN索引:

CREATE INDEX tbl_col_gin_trgm_idx ON tbl USING gin (col gin_trgm_ops);

有关:


2
这实际上是正确的答案。
vonPetrushev

9

该索引将无济于事,因为匹配开始时为'%'-BTREE索引只能匹配前缀,而查询开始时的通配符意味着没有固定的前缀要查找。

这就是为什么它要进行表扫描并针对查询字符串依次匹配每个记录。

您可能需要查看使用全文索引和文本匹配运算符,而不是使用当前的LIKE进行子字符串搜索。您可以在文档中找到有关全文搜索的更多信息:

http://www.postgresql.org/docs/8.4/static/textsearch-intro.html

实际上,我从该页面注意到LIKE显然从未使用过索引,这对我来说似乎很奇怪,因为它应该能够使用BTREE索引来解析非通配符前缀。一些快速测试表明该文档可能是正确的,但是在这种情况下,当您使用LIKE来解决查询时,没有大量的索引会有所帮助。


那就是我所担心的。还有另一种索引会有所帮助吗?如我所说,我影响查询本身的能力受到了一些限制。
David Eyk 2011年

此外,领导%功能是一项必要功能:客户服务代表需要它来查找客户帐户,尤其是在电子邮件地址中存在拼写错误时。
David Eyk,2011年

好吧,在对LIKE和全文索引进行了一些研究之后,我开始明白您的意思了。
大卫·艾克

目前,我已经找到一种抑制通配符的方法。事实证明,如果使用适当的运算符类创建索引,则可以将索引与LIKE一起使用。文档在这里:postgresql.org/docs/8.4/static/indexes-opclass.html
David Eyk 2011年

另外,请检查您的数据库是否有膨胀。如果您在该表中有很多膨胀,则将需要很长时间才能对其进行顺序扫描。如果您有一些停机时间,只需将其群集在主键上,看看它是否变得更快。如果要检查膨胀,可以运行分析然后在此处运行查询:wiki.postgresql.org/wiki/Show_database_bloat。有关更准确的值,请参阅该页面的底部。
Scott Marlowe
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.