为啥 Spanner 在 LIKE 中使用下划线执行全表扫描,而使用 % 则利用索引?

Posted

技术标签:

【中文标题】为啥 Spanner 在 LIKE 中使用下划线执行全表扫描,而使用 % 则利用索引?【英文标题】:Why Spanner performs full table scan using a underscore in a LIKE, while using % leverages the index?为什么 Spanner 在 LIKE 中使用下划线执行全表扫描,而使用 % 则利用索引? 【发布时间】:2019-11-25 22:24:39 【问题描述】:

在查询中,如果我在主键上使用LIKE '<value>%',它会使用索引执行良好:

Operator | Rows returned | Executions | Latency
-- | -- | -- | --
 Serialize Result   32  1   1.80 ms
 Sort   32  1   1.78 ms
 Hash Aggregate 32  1   1.73 ms
 Distributed union  32  1   1.61 ms
 Hash Aggregate 32  1   1.56 ms
 Distributed union  128 1   1.34 ms
 Compute    -   -   -
 FilterScan 128 1   1.33 ms
 Table Scan: <tablename>    128 1   1.30 ms

尽管如此,使用LIKE '&lt;value&gt;_' 执行全表扫描:

Operator | Rows returned | Executions | Latency
-- | -- | -- | --
Serialize Result | 32 | 1 | 76.27 s
Sort | 32 | 1 | 76.27 s
Hash Aggregate | 32 | 1 | 76.27 s
Distributed union | 32 | 1 | 76.27 s
Hash Aggregate | 32 | 2 | ~72.18 s
Distributed union | 128 | 2 | ~72.18 s
Compute | - | - | -
FilterScan | 128 | 2 | ~72.18 s
Table Scan: <tablename> (full scan: true) | 13802624 | 2 | ~69.97 s

查询如下所示:

SELECT
    'aggregated-quadkey AS quadkey' AS quadkey, day,
    SUM(a_value_1), SUM(a_value_2), AVG(a_value_3), SUM(a_value_4), SUM(a_value_5), AVG(a_value_6), AVG(a_value_6), AVG(a_value_7), SUM(a_value_8), SUM(a_value_9), AVG(a_value_10), SUM(a_value_11), SUM(a_value_12), AVG(a_value_13), AVG(a_value_14), AVG(a_value_15), SUM(a_value_16), SUM(a_value_17), AVG(a_value_18), SUM(a_value_19), SUM(a_value_20), AVG(a_value_21), AVG(a_value_22), AVG(a_value_23)
FROM <tablename>
WHERE quadkey LIKE '03201012212212322_'
GROUP BY quadkey, day ORDER BY day

【问题讨论】:

【参考方案1】:

对于匹配 LIKE 模式 (column LIKE 'xxx%') 的前缀,查询优化器在内部将条件转换为 STARTS_WITH(column, 'xxx'),然后使用索引。

所以原因可能是因为查询优化器不够聪明 转换匹配 LIKE 模式的精确长度前缀

column LIKE 'xxx_'

成组合条件:

(STARTS_WITH(column, 'xxx') AND CHAR_LENGTH(column)=4)

类似的模式,如

`column LIKE 'abc%def'`

没有优化到组合条件:

`(STARTS_WITH(column,'abc') AND ENDS_WITH(column,'def'))`.

您始终可以通过使用上述条件优化 SQL 生成中的查询来解决此问题。

(假设 LIKE 模式是查询中的字符串值,而不是参数 - LIKE 使用参数无法优化,因为该模式在查询编译时未知。)

【讨论】:

是的,这是我的假设。我只是想知道这是否只是优化器限制,或者是否存在我看不到的任何内在问题(_% 更具限制性,所以它的性能应该不会更差)。 PD:是的,模式是一个值,而不是一个参数。【参考方案2】:

感谢您报告此问题!我已经在积压中添加了这个重写。同时,您可以按照 RedPandaCurios 的建议使用 STARTS_WITH 和 CHAR_LENGTH 来解决此问题。

【讨论】:

您可以在How to Answer 上找到一些信息来升级您的答案。知道您是如何得出答案的可能会很有趣。 yongchul 在 Google Cloud Spanner 上工作(来自他的个人资料)

以上是关于为啥 Spanner 在 LIKE 中使用下划线执行全表扫描,而使用 % 则利用索引?的主要内容,如果未能解决你的问题,请参考以下文章

LIKE运算符

为啥我不能在存储函数中使用 Like?

SQL --------------- like 查询

PostgreSQL LIKE 子句

PostgreSQL LIKE 子句

sql模糊查询