我首先要说的是,如果值以通配符开头,我知道您不能将索引用于 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/