PostgreSQL LIKE查询性能变化

时间:2009-10-14 14:37:58

标签: postgresql indexing pattern-matching query-optimization sql-like

对于我的数据库中的特定表的LIKE查询,我的响应时间变化很大。有时我会在200-400毫秒内得到结果(非常可接受),但有时可能需要30秒才能返回结果。

我理解LIKE查询是非常耗费资源的,但我不明白为什么响应时间会有这么大的差异。我在owner1字段上构建了一个btree索引,但我不认为它对LIKE个查询有帮助。有人有什么想法吗?

示例SQL:

SELECT gid, owner1 FORM parcels
WHERE owner1 ILIKE '%someones name%' LIMIT 10

我也试过了:

SELECT gid, owner1 FROM parcels
WHERE lower(owner1) LIKE lower('%someones name%') LIMIT 10

SELECT gid, owner1 FROM parcels
WHERE lower(owner1) LIKE lower('someones name%') LIMIT 10

结果相似。
表行数:约95,000。

8 个答案:

答案 0 :(得分:250)

FTS不支持LIKE

previously accepted answer不正确。对于LIKE运算符,Full Text Search及其全文索引,它有自己的运算符,不适用于任意字符串。它基于词典和词干来运行单词。它 支持前缀匹配,但不支持LIKE运算符:

LIKE

的Trigram索引

安装附加模块pg_trgm,该模块为GIN and GiST trigram indexes提供运算符类,以支持所有LIKEILIKE模式,而不仅仅是左锚定的:

示例索引:

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

或者:

CREATE INDEX tbl_col_gist_trgm_idx ON tbl USING gist (col gist_trgm_ops);

示例查询:

SELECT * FROM tbl WHERE col LIKE '%foo%';   -- leading wildcard
SELECT * FROM tbl WHERE col ILIKE '%foo%';  -- works case insensitively as well

八卦?短字符串怎么样?

索引值中少于3个字母的字词仍可使用。 The manual:

  

每个单词被认为有两个前缀空格和一个空格   确定字符串中包含的三元组时的后缀。

搜索少于3个字母的模式? The manual:

  

对于LIKE和正则表达式搜索,请记住a   没有可提取三卦的模式将退化为全索引扫描。

意思是,索引/位图索引扫描仍然有效(预备语句的查询计划不会中断),它只是不会给你带来更好的性能。通常没有大的损失,因为单字母或双字母字符串几乎没有选择性(超过基础表匹配的百分之几)并且索引支持不会提高性能,因为全表扫描更快。


text_pattern_ops用于前缀匹配

对于左锚定模式(没有前导通配符),您可以获得适合btree索引operator class的最佳模式:text_pattern_opsvarchar_pattern_ops。标准Postgres的内置功能,无需额外的模块。类似的表现,但指数要小得多。

示例索引:

CREATE INDEX tbl_col_text_pattern_ops_idx ON tbl(col text_pattern_ops);

示例查询:

SELECT * FROM tbl WHERE col LIKE 'foo%';  -- no leading wildcard

,如果您应该使用'C'区域设置(有效没有区域设置)运行数据库,那么一切都按照无论如何要使用字节顺序,使用默认运算符类的普通btree索引可以完成这项工作。

dba.SE上这些相关答案的更多细节,解释,示例和链接:

答案 1 :(得分:7)

可能快速的锚定模式具有区分大小写,可以使用索引。即,在匹配字符串的开头没有通配符,因此执行程序可以使用索引范围扫描。 (the relevant comment in the docs is here)Lower和ilike也将失去使用索引的能力,除非您专门为此目的创建索引(参见functional indexes)。

如果您想在字段中间搜索字符串,则应该查看full texttrigram indexes。第一个是Postgres核心,另一个是contrib模块。

答案 2 :(得分:4)

你可以在PostgreSQL中安装Wildspeed,一种不同类型的索引。 Wildspeed确实可以使用%word%通配符,没问题。缺点是指数的大小,这可能很大,非常大。

答案 3 :(得分:2)

请执行下面提到的查询以改进postgresql中的LIKE查询性能。 为更大的表创建这样的索引:

CREATE INDEX <indexname> ON <tablename> USING btree (<fieldname> text_pattern_ops)

答案 4 :(得分:1)

我最近在一个包含200000条记录的表上遇到了类似的问题,我需要重复执行LIKE查询。就我而言,正在搜索的字符串是固定的。其他领域各不相同。因此,我得以重写:

SELECT owner1 FROM parcels
WHERE lower(owner1) LIKE lower('%someones name%');

CREATE INDEX ix_parcels ON parcels(position(lower('someones name') in lower(owner1)));

SELECT owner1 FROM parcels
WHERE position(lower('someones name') in lower(owner1)) > 0;

当查询快速返回并验证索引是否与EXPLAIN ANALYZE一起使用时,我感到很高兴:

 Bitmap Heap Scan on parcels  (cost=7.66..25.59 rows=453 width=32) (actual time=0.006..0.006 rows=0 loops=1)
   Recheck Cond: ("position"(lower(owner1), 'someones name'::text) > 0)
   ->  Bitmap Index Scan on ix_parcels  (cost=0.00..7.55 rows=453 width=0) (actual time=0.004..0.004 rows=0 loops=1)
         Index Cond: ("position"(lower(owner1), 'someones name'::text) > 0)
 Planning time: 0.075 ms
 Execution time: 0.025 ms

答案 5 :(得分:0)

您的查询可能无法使用您创建的索引,因为:

1)您的LIKE标准以通配符开头。

2)您使用了LIKE标准的函数。

答案 6 :(得分:0)

对于它的价值, Django ,ORM倾向于对所有UPPER(text)查询使用LIKE使其不区分大小写,

UPPER(column::text)上添加索引大大加快了我的系统速度,

对于前导%,是的,将不使用索引。请参阅此博客以获取详细说明:

https://use-the-index-luke.com/sql/where-clause/searching-for-ranges/like-performance-tuning

答案 7 :(得分:0)

无论何时在具有LIKE,ILIKE,upper,lower等功能的列上使用子句,postgres都不会考虑您的普通索引。它将对通过每一行的表进行全面扫描,因此会很慢。

正确的方法是根据您的查询创建新索引。例如,如果我要匹配不区分大小写的列,并且我的列是varchar。然后您可以这样做。

create index ix_tblname_col_upper on tblname (UPPER(col) varchar_pattern_ops);

类似地,如果您的专栏是文本,那么您将执行以下操作

create index ix_tblname_col_upper on tblname (UPPER(col) text_pattern_ops);

类似地,您可以将函数的上半部更改为所需的任何其他函数。