后缀匹配 LIKE '%suffix' 一定走全表扫描,因为B+树索引按字典序从左到右构建,无法倒序定位末尾字符;字符串反转+前缀索引是成本最低的可行解。

为什么后缀匹配 LIKE '%suffix' 一定走全表扫描
MySQL 的 B+ 树索引是按字典序从左到右构建的,name LIKE '%suffix' 要求匹配末尾字符,但索引无法“倒着用”。即使 name 上有普通索引,优化器也直接放弃,因为无法定位起始位置。这不是配置或写法问题,而是索引结构决定的硬限制。
字符串反转 + 前缀索引是成本最低的可行解
核心思路:把后缀问题转成前缀问题。对原始字段值做反转(如 'hello@example.com' → 'moc.elpmaxe@olleh'),存入新列(比如 email_reversed),再对该列建普通 B+ 树索引。查询时,把搜索词也反转,用 LIKE 前缀查。
- 插入/更新时需同步维护反转列,可用
BEFORE INSERT/UPDATE触发器自动完成 - 反转函数用
REVERSE(),不要手写逻辑;例如:SET NEW.email_reversed = REVERSE(NEW.email); - 索引必须建在反转列上:
CREATE INDEX idx_email_rev ON users(email_reversed); - 查询写法必须严格对应:
SELECT * FROM users WHERE email_reversed LIKE REVERSE('%pierre');→ 实际应写为email_reversed LIKE 'erriep%'(注意:REVERSE('%pierre')是'erriep%',不是'%erriep')
容易踩的坑:反转列内容与查询条件不匹配
这是线上最常出错的地方。常见错误包括:
- 查询时忘了反转搜索词,写成
WHERE email_reversed LIKE '%erriep'—— 这又变回后缀匹配,索引失效 - 误用
REVERSE()在WHERE子句里包裹整个条件,如WHERE REVERSE(email) LIKE '%erriep'—— 函数作用于列,必然无法走索引 - 反转列未加索引,或索引建在原字段上(
email),对email_reversed查询毫无帮助 - 业务中存在大小写混用,而 MySQL 默认不区分大小写排序(
utf8mb4_0900_as_cs除外),导致REVERSE('Pierre')和REVERSE('pierre')结果不同,但索引可能只覆盖一种
它不适合什么场景
反转方案轻量、纯 MySQL、无需中间件,但有明确边界:
- 只适合「确定后缀」的固定模式查询,比如查所有
@gmail.com、.cn、_backup结尾的记录;不适合LIKE '%abc%'这种中缀模糊 - 不解决中文分词需求,
REVERSE('张三')是'三张',但用户搜“张”或“三”无法通过单次反转索引覆盖 - 如果字段本身很长(如超 255 字符),反转列也要同样长,B+ 树索引深度增加,范围扫描性能会下降
- 高并发写入下,触发器带来额外开销,且需确保事务一致性 —— 反转列必须和原字段在同一事务中更新
实际生效的关键就两点:反转列值必须 100% 精确可推导,索引必须落在这个列上。少一个环节,就退回全表扫描。



















