1. 问题背景:为什么 LIKE '%abc' 查询慢如蜗牛?
在日常开发中,我们经常会遇到需要根据字符串后缀进行查询的场景。比如查找所有Gmail邮箱用户(WHERE email LIKE '%@gmail.com'),或者查询尾号为1234的手机号(WHERE phone LIKE '%1234')。这类查询在数据量小的时候可能表现尚可,但当数据量达到百万级时,查询速度就会急剧下降。
关键问题:B+树索引是按照从左到右的顺序构建的,当使用LIKE '%abc'这样的查询条件时,由于通配符在最左侧,索引无法发挥作用,数据库只能进行全表扫描(Full Table Scan)。
我曾在实际项目中遇到过这样的案例:一个用户表有800万条记录,查询尾号为8888的手机号需要8-12秒才能返回结果。通过EXPLAIN分析发现,type显示为ALL,表示确实在进行全表扫描。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心原理:逆向思维解决索引失效问题
2.1 B+树索引的工作原理
B+树索引之所以高效,是因为它按照字典序组织数据。以字符串"apple"、"banana"、"cherry"为例:
- 正序存储时,索引按照a-p-p-l-e的顺序构建
- 查询以'e'结尾的单词时,由于'e'出现在字符串末尾,索引无法提供有效帮助
2.2 反向存储的魔法
反向存储的核心思想是将字符串倒置后存储:
- "apple" → "elppa"
- "banana" → "ananab"
- "cherry" → "yrrehc"
此时,查询以'e'结尾的原始字符串,就变成了查询以'e'开头的反向字符串:
sql复制-- 原始查询(无法使用索引)
SELECT * FROM fruits WHERE name LIKE '%e';
-- 优化后查询(可以使用索引)
SELECT * FROM fruits WHERE reversed_name LIKE 'e%';
这种转换使得原本无法使用索引的后缀查询,变成了可以利用索引的前缀查询。
3. MySQL 5.7+ 的优雅实现方案
3.1 虚拟生成列(Generated Columns)
MySQL 5.7引入的虚拟生成列功能,让我们无需修改应用层代码就能实现反向存储:
sql复制
