Performance 低人一等
以下两个查询组件的性能比较如何 较低的类Performance 低人一等,performance,postgresql,pattern-matching,database-performance,Performance,Postgresql,Pattern Matching,Database Performance,以下两个查询组件的性能比较如何 较低的类 ... LOWER(description) LIKE '%abcde%' ... ... description iLIKE '%abcde%' ... my_test_db=# EXPLAIN ANALYZE SELECT "books".* FROM "books" WHERE "books"."published" = 'f' and (LOWER(description) LIKE '%abcde%') ;
... LOWER(description) LIKE '%abcde%' ...
... description iLIKE '%abcde%' ...
my_test_db=# EXPLAIN ANALYZE SELECT "books".* FROM "books" WHERE "books"."published" = 'f' and (LOWER(description) LIKE '%abcde%') ;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------
Seq Scan on books (cost=0.00..32420.14 rows=1600 width=117) (actual time=938.627..4114.038 rows=3 loops=1)
Filter: ((NOT published) AND (lower(description) ~~ '%abcde%'::text))
Rows Removed by Filter: 1000006
Total runtime: 4114.098 ms
my_test_db=# EXPLAIN ANALYZE SELECT "books".* FROM "books" WHERE "books"."published" = 'f' and (description iLIKE '%abcde%') ;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------
Seq Scan on books (cost=0.00..29920.11 rows=100 width=117) (actual time=1147.612..4986.771 rows=3 loops=1)
Filter: ((NOT published) AND (description ~~* '%abcde%'::text))
Rows Removed by Filter: 1000006
Total runtime: 4986.831 ms
我喜欢
... LOWER(description) LIKE '%abcde%' ...
... description iLIKE '%abcde%' ...
my_test_db=# EXPLAIN ANALYZE SELECT "books".* FROM "books" WHERE "books"."published" = 'f' and (LOWER(description) LIKE '%abcde%') ;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------
Seq Scan on books (cost=0.00..32420.14 rows=1600 width=117) (actual time=938.627..4114.038 rows=3 loops=1)
Filter: ((NOT published) AND (lower(description) ~~ '%abcde%'::text))
Rows Removed by Filter: 1000006
Total runtime: 4114.098 ms
my_test_db=# EXPLAIN ANALYZE SELECT "books".* FROM "books" WHERE "books"."published" = 'f' and (description iLIKE '%abcde%') ;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------
Seq Scan on books (cost=0.00..29920.11 rows=100 width=117) (actual time=1147.612..4986.771 rows=3 loops=1)
Filter: ((NOT published) AND (description ~~* '%abcde%'::text))
Rows Removed by Filter: 1000006
Total runtime: 4986.831 ms
根据我的测试(每次查询10次),LOWER
LIKE
大约比iLIKE
快17%
解释
我创建了一百万行,其中包含一些随机混合文本数据:
require 'securerandom'
inserts = []
1000000.times do |i|
inserts << "(1, 'fake', '#{SecureRandom.urlsafe_base64(64)}')"
end
sql = "insert into books (user_id, title, description) values #{inserts.join(', ')}"
ActiveRecord::Base.connection.execute(sql)
(是的,我从其他测试中多了九行-没问题。)
查询和结果示例:
my_test_db=# SELECT "books".* FROM "books" WHERE "books"."published" = 'f'
my_test_db=# and (LOWER(description) LIKE '%abcde%') ;
id | user_id | title | description | published
---------+---------+-------+----------------------------------------------------------------------------------------+------
1232322 | 1 | fake | 5WRGr7oCKABcdehqPKsUqV8ji61rsNGS1TX6pW5LJKrspOI_ttLNbaSyRz1BwTGQxp3OaxW7Xl6fzVpCu9y3fA | f
1487103 | 1 | fake | J6q0VkZ8-UlxIMZ_MFU_wsz_8MP3ZBQvkUo8-2INiDIp7yCZYoXqRyp1Lg7JyOwfsIVdpPIKNt1uLeaBCdelPQ | f
1817819 | 1 | fake | YubxlSkJOvmQo1hkk5pA1q2mMK6T7cOdcU3ADUKZO8s3otEAbCdEcmm72IOxiBdaXSrw20Nq2Lb383lq230wYg | f
较低级别的LIKE结果
... LOWER(description) LIKE '%abcde%' ...
... description iLIKE '%abcde%' ...
my_test_db=# EXPLAIN ANALYZE SELECT "books".* FROM "books" WHERE "books"."published" = 'f' and (LOWER(description) LIKE '%abcde%') ;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------
Seq Scan on books (cost=0.00..32420.14 rows=1600 width=117) (actual time=938.627..4114.038 rows=3 loops=1)
Filter: ((NOT published) AND (lower(description) ~~ '%abcde%'::text))
Rows Removed by Filter: 1000006
Total runtime: 4114.098 ms
my_test_db=# EXPLAIN ANALYZE SELECT "books".* FROM "books" WHERE "books"."published" = 'f' and (description iLIKE '%abcde%') ;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------
Seq Scan on books (cost=0.00..29920.11 rows=100 width=117) (actual time=1147.612..4986.771 rows=3 loops=1)
Filter: ((NOT published) AND (description ~~* '%abcde%'::text))
Rows Removed by Filter: 1000006
Total runtime: 4986.831 ms
iLIKE的结果
... LOWER(description) LIKE '%abcde%' ...
... description iLIKE '%abcde%' ...
my_test_db=# EXPLAIN ANALYZE SELECT "books".* FROM "books" WHERE "books"."published" = 'f' and (LOWER(description) LIKE '%abcde%') ;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------
Seq Scan on books (cost=0.00..32420.14 rows=1600 width=117) (actual time=938.627..4114.038 rows=3 loops=1)
Filter: ((NOT published) AND (lower(description) ~~ '%abcde%'::text))
Rows Removed by Filter: 1000006
Total runtime: 4114.098 ms
my_test_db=# EXPLAIN ANALYZE SELECT "books".* FROM "books" WHERE "books"."published" = 'f' and (description iLIKE '%abcde%') ;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------
Seq Scan on books (cost=0.00..29920.11 rows=100 width=117) (actual time=1147.612..4986.771 rows=3 loops=1)
Filter: ((NOT published) AND (description ~~* '%abcde%'::text))
Rows Removed by Filter: 1000006
Total runtime: 4986.831 ms
数据库信息披露
博士后版本:
my_test_db=# select version();
version
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
PostgreSQL 9.2.4 on x86_64-apple-darwin12.4.0, compiled by i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00), 64-bit
排序规则设置:
my_test_db=# select datcollate from pg_database where datname = 'my_test_db';
datcollate
-------------
en_CA.UTF-8
表定义:
my_test_db=# \d books
Table "public.books"
Column | Type | Modifiers
-------------+-----------------------------+-------------------------------------------------------
id | integer | not null default nextval('books_id_seq'::regclass)
user_id | integer | not null
title | character varying(255) | not null
description | text | not null default ''::text
published | boolean | not null default false
Indexes:
"books_pkey" PRIMARY KEY, btree (id)
答案取决于许多因素,比如Postgres版本、编码和语言环境——尤其是
像“%abc%”这样的裸表达式lower(description)
通常比像“%abc%”这样的description(description)要快一点,并且两者都比等价的正则表达式快一点:description~*'abc'
。这对于顺序扫描很重要,其中必须为每个测试行计算表达式
但是对于您在回答中演示的大型表格,您肯定会使用索引。对于任意模式(不仅仅是左锚定模式),我建议使用附加模块创建三角图索引。然后我们讨论毫秒而不是秒,上面表达式之间的差异为空
GIN和GiST索引(使用GIN\u trgm\u ops
或GiST\u trgm\u ops
操作符类)支持,如(~
)、ILIKE
(~*
)、~*
(以及其他一些变体)等。如果在description
上有一个trigram GIN索引(通常比GiST大,但读取速度更快),您的查询将使用description类似于“不区分大小写的模式”
相关的:
Postgres中模式匹配的基础知识:
当使用上述三元索引时,通常更实用:
description ILIKE '%abc%'
或者使用不区分大小写的regexp运算符(不带%
通配符):
(说明)
上的索引不支持对下部(说明)
的查询,例如:
lower(description) LIKE '%abc%'
反之亦然
使用lower(description)
上的谓词,表达式索引是更好的选择
在所有其他情况下,最好使用(description)
上的索引,因为它支持区分大小写和不区分大小写的谓词。在我的rails项目中ILIKE
比LOWER LIKE
快近10倍,我在实体上添加了GIN
索引。name
列
> Entity.where("LOWER(name) LIKE ?", name.strip.downcase).limit(1).first
Entity Load (2443.9ms) SELECT "entities".* FROM "entities" WHERE (lower(name) like 'baidu') ORDER BY "entities"."id" ASC LIMIT $1 [["LIMIT", 1]]
GIN索引确实有助于提高ILIKE的性能,因为您的测试用例是单面的,数据中只有大写字母。此外,在实际应用程序中,您将使用索引进行操作,这将改变整个评估。基本细节未披露:Postgres版本、排序规则设置、确切的表定义。@ErwinBrandstetter我重新运行了测试,并更新了答案以反映混合案例数据。我还添加了有关数据库的详细信息<代码>较低
仍然比iLIKE
快17%
(比25%
低一点)。+1现在好多了。不过,我不会说“x比y快17%”,因为这只适用于您的特定测试用例。顺便说一句,字符串的长度也是相关的。@ErwinBrandstetter-我意识到精确的百分比结果会有所不同,但我也认为仅仅说“x比y快”太过开放。我认为,通过你的评论,那些足够好奇的人会得到一个更完整的画面。顺便问一下,您是否注意到字符串长度、排序规则设置或其他一些情况,这些情况会持续导致i像LOWER那样执行LOWER
?不。不过,不确定。我经常看到更接近的结果。在Postgres 9.1中的一个实际表格上运行了一个快速测试,其中有105万行和实际的“描述”,COLLATON de_AT.UTF-8,OS Debian Linux。LOWER/LIKE快了约2%。你的目标是什么?你有一个缓慢的SQL命令,你想加快它还是只是一个关于PostgreSQL?堆栈溢出的一般问题,还没有(现在?)标签(除非你问C或C++问题)。@ MartinStrejc我的目标只是PostgreSQL上的一个一般问题。在选择这两个盒子外的解决方案时,我想知道使用哪一个。我会考虑你的建议,虽然我只是想澄清我的意图是比较这两个盒子外的解决方案。我重新运行了测试,并更新了答案,以反映混合案例数据。我还添加了有关数据库的详细信息。结果是LOWER
LIKE
仍然比iLIKE
快约17%
(从25%
下降)。值得注意的是,报告的17%用于无索引的顺序扫描。我得到了2%的相似测试。如果在设置中添加了三元索引,这两种方法都不适用-这会消除差异。感谢您的跟进。您认为在您的回答中添加此评论公平吗?-我认为选择的答案应该得出这样的结论:LOWER LIKE
更快(除非添加了三元索引,在这种情况下,正如您所说,没有区别)。。。但关键是人们应该使用的是LOWER LIKE
,而不是i LIKE
,后者要么是等效的,要么是较慢的。@user664833:不,不是一般的。我在上面解释过。like和ilike的使用不取决于您如何存储数据吗?如果数据库中有'Joe',则需要降低两次:'lower(input)LIKE lower('%Joe%')。我想这就是为什么要创建ILIKE的原因,name
上的索引不支持对lower(name)的查询