前缀匹配(如'张%')能利用B+树索引有序性走索引,后缀或中缀匹配(如'%error%')因无法跳过开头查中间片段必须全表扫描;前缀索引长度需权衡区分度与体积,函数索引更精准但仅限MySQL 8.0+且不优化LIKE。

为什么 WHERE name LIKE '张%' 走了索引,但 WHERE content LIKE '%error%' 一定不走?
因为只有前缀匹配(LIKE 'abc%')能利用 B+ 树索引的有序性;后缀或中缀匹配(%abc 或 %abc%)必须全表扫描。MySQL 的普通索引对字符串是按完整值排序的,没法跳过开头直接找中间片段。
所以“长字符串查询优化”的起点不是“怎么建索引”,而是先确认:你真需要模糊匹配中间内容?还是其实只需要前缀过滤?
- 日志字段
message存的是整行日志,但业务上只查“以 ‘WARN’ 开头”或“以 ‘timeout’ 结尾”——前者可建前缀索引,后者不行,得换方案(比如加标志字段) - 用户昵称
nike_name查询LIKE '小明%',建前缀索引有效;但查LIKE '%小明%',前缀索引完全无效 - 如果必须支持中缀搜索,
FULLTEXT或外部搜索引擎(Elasticsearch)才是正解,别硬扛
ALTER TABLE t ADD INDEX idx_content_20 (content(20)) 中的 20 怎么定?
不是越长越好,也不是拍脑袋定。这个长度本质是在「索引体积」和「区分度(selectivity)」之间找平衡点:太短,大量值前 20 字符相同(比如 URL 都以 https://example.com/ 开头),索引失效;太长,索引变大、写入变慢、缓存命中率下降。
实操步骤:
- 先用
SELECT COUNT(DISTINCT LEFT(content, 10)) / COUNT(*) FROM t;算 10 字符前缀的选择性(接近 1 最好) - 逐步试 15、20、25……直到选择性 ≥ 0.9,且
SHOW INDEX FROM t显示该索引实际被EXPLAIN用上 - 注意:如果表里有大量空值或极短字符串(如
''或'a'),前缀长度不能超过字段实际最小非空长度,否则索引项全为NULL或重复值
前缀索引会导致 ORDER BY 和 GROUP BY 失效吗?
会,而且很隐蔽。前缀索引只存储截断后的值,MySQL 无法用它做完整排序或分组依据。
比如建了 INDEX idx_title_15 (title(15)),执行 SELECT * FROM article ORDER BY title LIMIT 10,即使 title 是主排序字段,MySQL 仍可能放弃索引、改用 filesort。
- 验证方法:用
EXPLAIN看Extra列是否出现Using filesort或Using temporary - 如果业务强依赖排序/分组,优先考虑完整列索引,或把排序字段冗余成较短的规范化字段(如
title_prefix CHAR(15))再建索引 - JSON 字段或超长文本字段(
MEDIUMTEXT)上建前缀索引后,ORDER BY基本必然退化,别抱幻想
MySQL 8.0+ 的函数索引能替代前缀索引吗?
能,而且更精准——但仅限于明确知道截取逻辑的场景,比如统一取前 20 字、或按分隔符截取。
例如:CREATE INDEX idx_url_host ON logs ((SUBSTRING_INDEX(url, '/', 3))),就比 url(20) 更可靠:避免截在域名中间,也避开协议头长度差异问题。
- 函数索引要求 MySQL ≥ 8.0.13,且字段不能是
TEXT/BLOB(除非用生成列间接实现) - 函数必须是 deterministic(确定性),像
NOW()、RAND()不行;SUBSTRING()、LEFT()、REPLACE()可以 - 注意函数索引不支持
LIKE的通配符优化,它只加速函数计算结果的等值查询,比如WHERE SUBSTRING(content,1,20) = 'ERROR: connection'
真正难的从来不是选 15 还是 20,而是得想清楚:这个字段到底承载什么语义?是当标识符用,还是当内容检索用?前者适合前缀或函数索引,后者大概率得换技术栈。










