PostgreSQL LIKE查询性能变化

polhcujo  于 2022-11-04  发布在  PostgreSQL
关注(0)|答案(8)|浏览(401)

我发现对数据库中某个特定表的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

结果相似。
表行数:约九万五千人。

c9qzyr3d

c9qzyr3d1#

FTS不支持LIKE

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

LIKE的三元组索引

安装额外的模块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**);
  • GiST和GIN指数之间的差异

示例查询:

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

三角形?更短的字符串呢?

索引值中少于3个字母的单词仍然有效。手册:
当确定字符串中包含的三元组时,每个单词被认为具有两个空格前缀和一个空格后缀。
而搜索不到3个字母的模式?手册:
对于LIKE和正则表达式搜索,请记住,没有可提取三元组的模式将退化为全索引扫描。
这意味着,索引/位图索引扫描仍然可以工作(预准备语句的查询计划不会中断),只是不会为您带来更好的性能。通常没有什么大的损失,因为1个或2个字母的字符串几乎没有选择性(超过底层表匹配的百分之几),而且索引支持一开始也不会提高性能(很多),因为全表扫描更快。

前缀匹配

没有前导通配符的搜索模式:col LIKE 'foo%' .

^@运算符/starts_with()函数

引用Postgres 11的发行说明:
添加前缀匹配运算符文本^@ text,SP-GiST(Ildus Kurbangaliev)支持此操作
这类似于使用带有btree索引的var LIKE 'word%',但效率更高。
示例查询:

SELECT * FROM tbl WHERE**col ^@ 'foo'**;  -- no added wildcard

但是,在正确记录planner support is improved inPostgres 15^@运算符之前,运算符和函数的潜力仍然有限。
如果使用C排序规则,则允许^@ starts-with运算符和starts_with()函数使用btree索引(Tom Lane)
以前,这些只能使用SP-GiST索引。

COLLATE "C"

从Postgres 9.1开始,带有COLLATE "C"的索引提供了与下面描述的操作符类text_pattern_ops相同的功能。请参见:

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' 语言环境(实际上 no 语言环境)运行数据库,那么所有内容都将根据字节顺序排序,并使用带有默认操作符类的普通btree索引来完成这项工作。

进一步阅读


gab6jxml

gab6jxml2#

快速的可能是锚定的模式,区分大小写,比如可以使用索引。也就是说,在匹配字符串的开头没有通配符,所以执行程序可以使用索引范围扫描。(the relevant comment in the docs is here)Lower和Iike也会失去使用索引的能力,除非你专门为此创建了一个索引(参见functional indexes)。
如果你想在字段中间搜索字符串,你应该查看full texttrigram indexes。第一个在Postgres核心中,另一个在contrib模块中。

chhkpiq4

chhkpiq43#

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

ig9co6j1

ig9co6j14#

我最近遇到了一个类似的问题,一个包含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
9q78igpj

9q78igpj5#

当你在一个列上使用一个子句,比如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);

同样,您可以将函数upper更改为所需的任何其他函数。

sy5wg1nm

sy5wg1nm6#

请执行下面提到的查询以提高postgresql中的LIKE查询性能。为较大的表创建如下索引:

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

f1tvaqid7#

值得注意的是,DjangoORM倾向于对所有LIKE查询使用UPPER(text),使其不区分大小写,
UPPER(column::text)上添加索引大大加快了我的系统速度,这与其他任何事情都不一样。
至于前导%,是的,它不会使用索引。请看这个博客的一个伟大的解释:
https://use-the-index-luke.com/sql/where-clause/searching-for-ranges/like-performance-tuning

jexiocij

jexiocij8#

您的like查询可能无法使用您创建的索引,因为:
1)您的LIKE条件以通配符开头。
2)您使用了一个带有LIKE条件的函数。

相关问题