在存储过程中用LIKE做模糊搜索,性能好坏取决于模式写法、索引匹配及是否全表扫描:'%keyword'需反向索引,'keyword%'可走B+树但依赖选择性,'%keyword%'应换ES等专用引擎,且须校验空值防'%%'。

在存储过程中用 LIKE 做模糊搜索,性能不取决于“是不是在存储过程里”,而取决于模式写法、字段索引结构、以及是否触发全表扫描——LIKE '%keyword' 和 LIKE '%keyword%' 在存储过程中照样无法走 B+ 树索引,硬写也白搭。
存储过程里 LIKE 'keyword%' 怎么写才真走索引
这是唯一能天然利用普通索引的场景,但容易因写法细节失效。
- 必须用
CONCAT(in_keyword, '%'),不能写成CONCAT('%', in_keyword)或CONCAT('%', in_keyword, '%')—— 后两者直接让优化器放弃索引 - 如果字段有字符集/校对规则(如
utf8mb4_0900_as_cs),传入参数也要保持一致,否则隐式转换导致索引失效 - 联合查询时,把高选择性条件放前面:例如
WHERE status = 'active' AND name LIKE CONCAT(in_keyword, '%'),对应索引应为(status, name),而非单列name - 前端或调用方传入前务必
TRIM(),空格会让' 张%'匹配不到'张三',还拉低索引选择性
为什么 LIKE '%keyword' 在存储过程里加索引也没用
B+ 树索引按字符串从左到右排序,LIKE '%keyword' 要求匹配末尾,数据库无法定位起点,只能扫全表。这不是 SQL 写得不够“巧”,是物理结构限制。
-
EXPLAIN中type显示ALL或index、key为NULL,就是没走索引的铁证 - 哪怕你用
FORCE INDEX强制指定,MySQL 仍会拒绝——它知道这条路根本走不通 -
UPPER(name) LIKE UPPER('%abc')这类函数包裹列的写法,100% 失效;统一小写存 + 直接name LIKE 'abc%'才可行 - MySQL 5.7 可建生成列
name_rev VARCHAR(255) AS (REVERSE(name)) STORED+ 索引,然后查name_rev LIKE CONCAT(REVERSE(in_keyword), '%');8.0+ 更简单:CREATE INDEX idx_name_rev ON t((REVERSE(name)))
什么时候该在存储过程里切到 MATCH ... AGAINST
当业务明确需要“包含关键词”“前后都模糊”“支持同义或拼音联想”时,LIKE 已不是优化问题,而是技术路径错误。
- 中文必须开
ngram插件并设ft_ngram_token_size = 2,否则搜单字(如“数”“据”)基本无结果 - 查询语句必须是
MATCH(col) AGAINST(in_keyword IN NATURAL LANGUAGE MODE),不能混用LIKE,也不能给in_keyword加% -
AGAINST不支持通配符前缀(*abc报错),但支持后缀(abc*在 BOOLEAN MODE 下可用) - 全文索引需提前建好,且字段类型要兼容(InnoDB 支持
VARCHAR/TEXT,但不支持JSON或ENUM)
存储过程里最容易被忽略的三个坑
很多慢查询不是因为没选对方案,而是卡在边界细节上。
-
_和%是LIKE的通配符,如果用户真想搜文件名report_2024,得先SET safe_key = REPLACE(REPLACE(in_keyword, '_', '\_'), '%', '\%'),再配合ESCAPE '\' - MyBatis 或其他 ORM 拼接时,若
in_keyword为空,可能生成LIKE '%%'—— 等于主动要求全表扫描 - 虚拟列和全文索引都需要
ALTER TABLE权限,上线前不检查账号权限,存储过程一执行就报错
真正难的不是写出能跑的 SQL,而是判断哪条路不该走:百万行以上、LIKE '%...%' 高频、响应要求 LIKE 就是自我消耗——该导出到 Elasticsearch,就别在 MySQL 里硬扛。

















