LIKE 的 MySQL 性能(无通配符)与 =

标签 mysql performance indexing

我首先要说的是,如果值以通配符开头,我知道您不能将索引用于 LIKE 查询。这不是关于那个的问题。我没有使用任何通配符。

在接受用户将通配符传递给查询的应用程序中,值被传递给查询的 LIKE 子句。我做了一些测试,得出的结论是,在搜索确切地址(因此没有通配符)时,查询运行速度比我使用 = 时慢。进行以下 2 个查询:

SELECT id FROM users WHERE email LIKE 'user@host.tld'

对比

SELECT id FROM users WHERE email = 'user@host.tld'

两个查询将返回完全相同的记录。在对两者进行 EXPLAIN 时,我可以看到它们都在使用电子邮件字段的索引。主要区别在于 LIKE 查询使用的是 RANGE 类型,而 = 查询使用的是 REF 类型。此外,RANGE 查询正在检查大约 1000 条记录,而 = 查询仅检查 1 条记录(表中的 200 万条记录)。

查询的配置文件是相同的,除了 LIKE 查询使用明显更多的时间来处理“发送数据”步骤,它实际检查 1000 条记录。所以基本上,查询速度较慢,因为它涉及更多数据。

我不明白的是,为什么要这样做?由于 RANGE 查询使用完全相同的索引并且应该从索引返回完全相同的匹配集,为什么它要检查更多行?这可能是关于范围查询如何使用索引与 ref 查询如何使用的内部结构的问题,但我似乎找不到任何关于它的详细信息。

最佳答案

问:为什么……[MySQL 优化器] 这样做?

答:

简短的回答是优化器转换LIKE= 中没有通配符操作。


MySQL 优化器只使用 ref访问=<=>比较。

MySQL优化器可以使用range访问更多操作,包括 = , <=> , < , <= , > , >= , BETWEEN , ...

像这样的谓词:col LIKE 'foo'正在处理等同于

 col >= 'foo' AND col <= 'foo'

我们看着它说,它与 col = 'foo' 相同,但优化器并不这么认为。如果我们使用通配符,优化器方法可能更有意义。例如

 col LIKE `foo%bar`

MySQL 可以使用 foo扫描的“范围”部分的部分,类似于此:

 col >= 'foo' AND col < 'fop'

MySQL 优化器可以使用索引范围扫描来满足 >=<比较。

(我在这里使用 fop 作为整理序列中最低的“较高权重”字符串的简单表示。我们不需要深入研究字符集和整理序列,只是作为我使用的简短理由'fop',带有 latin1_swedish_ci 整理序列...

SELECT HEX(WEIGHT_STRING(_latin1'foo' AS CHAR(3)))  AS ws_foo
     , HEX(WEIGHT_STRING(_latin1'fop' AS CHAR(3)))  AS ws_fop

而对于索引范围扫描找到的行,可以进行剩下的匹配,类似于

 SUBSTR(col,4) LIKE '%bar'

我并不是说这正是 MySQL 优化器的运作方式。 (我没有查看源代码。)

我建议 MySQL 优化器没有处理 'col LIKE 'foo''col = 'foo'相同,主要原因是通配符的可能性。


如果我们想要 col = 'foo'性能,我们应该写col = 'foo' .

当我们选择 LIKE 的灵 active 时,我们为范围扫描付出了代价比较。

当我们使用正则表达式 index 时,我们付出了更高的代价(完整索引扫描,col REGEXP '^foo$' 操作在 EXPLAIN 输出中) .


编辑

即使 EXPLAIN 计划中显示了差异,我也不认为这两个语句的性能有任何可衡量的差异:

 SELECT SQL_NO_CACHE id FROM users WHERE email LIKE 'user@host.tld'
 SELECT SQL_NO_CACHE id FROM users WHERE email =    'user@host.tld'

为了评估性能,我会连续四次(或更多次)运行语句,捕获每次语句运行的执行时间,并抛出第一次运行的结果。平均除第一次运行之外的运行时间。 (我们希望后续运行的执行时间彼此非常接近。)

请注意,数据库上的其他并发操作可能会影响我们正在测量的语句的性能。

关于LIKE 的 MySQL 性能(无通配符)与 =,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47519537/

相关文章:

php - 通过 Mysql Query 进行文本处理

javascript - jQuery 对象索引忽略 jQuery.filter()

PHP MySQL LIKE 命令无法正常工作

java - 我应该在循环中内联长代码,还是将其移动到单独的方法中?

oracle - 使用唯一约束来实现更简单的连接

indexing - 使用带有 GUID 主键的 Entity Framework Core 5.0.4 创建非聚集索引

nhibernate - 在nhibernate中设置聚集索引

mysql - 如何在 MySQL 中的两列中触发正则表达式条件?

mysql - 我存储在 mySQL DB 中的日期秒数通过 CrudRepository 更改

php - 如何连接具有不同关系的多个表并在 Laravel 5.3 中使用 attach()