【问题标题】:PostgreSQL performance difference between LIKE and regexLIKE 和正则表达式之间的 PostgreSQL 性能差异
【发布时间】:2015-06-10 15:17:23
【问题描述】:

有人能解释一下这些 SQL 之间如此大的性能差异吗?

SELECT count(*) as cnt FROM table WHERE name ~ '\*{3}'; -- Total runtime 12.000 - 18.000 ms
SELECT count(*) as cnt FROM table WHERE name ~ '\*\*\*'; -- Total runtime 12.000 - 18.000 ms
SELECT count(*) as cnt FROM table WHERE name LIKE '%***%'; -- Total runtime 5.000 - 7.000 ms

如您所见,LIKE 运算符和简单正则表达式之间的差异不止一倍(我认为 LIKE 运算符内部会转换为正则表达式,应该没有任何区别)

那里有将近 13000 行,“名称”列是“文本”类型。没有与表中定义的“名称”列相关的索引。

编辑:

解释他们每个人的分析:

EXPLAIN ANALYZE SELECT count(*) as cnt FROM datos WHERE nombre ~ '\*{3}';

Aggregate  (cost=894.32..894.33 rows=1 width=0) (actual time=18.279..18.280 rows=1 loops=1)
  ->  Seq Scan on datos (cost=0.00..894.31 rows=1 width=0) (actual time=0.620..18.266 rows=25 loops=1)
        Filter: (nombre ~ '\*{3}'::text)
Total runtime: 18.327 ms

EXPLAIN ANALYZE SELECT count(*) as cnt FROM datos WHERE nombre ~ '\*\*\*';
Aggregate  (cost=894.32..894.33 rows=1 width=0) (actual time=17.404..17.405 rows=1 loops=1)
  ->  Seq Scan on datos  (cost=0.00..894.31 rows=1 width=0) (actual time=0.608..17.396 rows=25 loops=1)
        Filter: (nombre ~ '\*\*\*'::text)
Total runtime: 17.451 ms

EXPLAIN ANALYZE SELECT count(*) as cnt  FROM datos WHERE nombre LIKE '%***%';
Aggregate  (cost=894.32..894.33 rows=1 width=0) (actual time=4.258..4.258 rows=1 loops=1)
  ->  Seq Scan on datos  (cost=0.00..894.31 rows=1 width=0) (actual time=0.138..4.249 rows=25 loops=1)
        Filter: (nombre ~~ '%***%'::text)
Total runtime: 4.295 ms

【问题讨论】:

  • 请显示explain analyze
  • @CraigRinger 我在问题文本中添加了对每个查询的解释分析
  • 运行正则表达式比较比应用虚拟LIKE 格式更昂贵。
  • @dmikam 我不同意 - 正则表达式更难解析且更难应用。如果您不同意我的观点 - 尝试同时实现 LIKE 和 PCRE 兼容引擎。然后比较哪个更费力,工作更慢。 “只匹配以 *** 结尾的字符串”---不,那里也有空格。
  • 这三个查询占用空间小,完全由内存/缓冲区提供服务。这就是为什么 CPU 成本在总成本中占主导地位的原因。一旦必须从磁盘中提取数据,成本将主要由搜索时间和 I/O 支配,查询的执行大致相同。 (至少:这是我所期望的)

标签: sql regex postgresql performance sql-like


【解决方案1】:

我不确定我是否应该像答案一样发布它...我做了一个粗略的比较,在 PHP 中做了类似的事情 - 使用正则表达式和简单的 strpos 过滤巨大的数组(作为 LIKE 的替代品)。代码:

// regex filter
$filteredRegex = array_filter($a,function($item){
    return preg_match('/000/',$item);
});
// substring search filter
$filteredStrpos = array_filter($a,function($item){
    return strpos($item,'000')!==FALSE;
});

因此,对这段代码进行基准测试会导致正则表达式过滤器及时将 strpos 的结果加倍,所以我可以假设正则表达式的 CPU 成本大约是简单搜索子字符串的两倍。

看起来@zerkms 有所有的原因:)

【讨论】:

  • 这是 SQL 而不是 PHP
  • @Dorian 这就是为什么我说“粗略比较在 PHP 中做出类似的东西”...
【解决方案2】:

text LIKE text 运算符 (~~) 由 like_match.c 中的特定 C 代码实现。它是完全独立于正则表达式的临时代码。看看 cmets,它显然经过特别优化,仅将 %_ 实现为通配符,并尽可能短路到出口,而正则表达式引擎要复杂几个数量级。

请注意,在您的测试用例中,就像正则表达式与 LIKE 相比不是最优的,LIKEstrpos(name, '***') > 0 相比可能不是最优的

strpos 是使用 Boyer–Moore–Horspool algorithm 实现的,该Boyer–Moore–Horspool algorithm 针对搜索文本中几乎没有部分匹配的大型子字符串进行了优化。

这些函数在内部进行了合理优化,但是当有多种方法可以实现同一目标时,选择可能的最佳方法仍然是调用者的工作。 PostgreSQL 不会根据该分析为我们分析匹配模式并将regexp 转换为LIKE 或将LIKE 转换为strpos

【讨论】:

  • 请注意,当模式包含或以文字字符串开头时,几个正则表达式引擎也会在使用 Boyer-Moore 算法启动引擎遍历之前测试字符串(作为更快失败的预优化)。我不知道 Postgres 是不是这样。
  • 我查看了like_match.c,它肯定比正则表达式简单得多(即使它仍然是递归的)。所以我最初的错误是认为 LIKE 在内部使用正则表达式或类似的东西实现。自然,strpos 算法甚至“更便宜”,但在我的粗略比较中,它证实了所选字符串分析算法的 CPU 成本可能会导致我所拥有的执行时间差异。
猜你喜欢
  • 1970-01-01
  • 2010-09-21
  • 2015-11-18
  • 2014-06-29
  • 2018-08-04
  • 2011-01-04
  • 1970-01-01
  • 1970-01-01
  • 2013-04-12
相关资源
最近更新 更多