索引就是数据库的“目录”,不是附加功能,而是查询加速的底层数据结构
索引在MySQL里不是锦上添花的配置项,而是直接决定
SELECT快慢的核心机制。它本质是一棵B+Tree(InnoDB默认),存储的是「字段值 + 对应行的物理位置」,而不是复制整行数据。没有索引时,MySQL只能全表扫描——比如查100万行里的某条记录,平均要读50万行;加了索引后,通常3~4次磁盘IO就能定位,性能差上百倍。关键点在于:索引生效的前提是查询条件能命中索引结构,否则它就只是白占空间、拖慢
INSERT/UPDATE/DELETE。
B-Tree索引为什么是主力?Hash和全文索引哪些场景不能用
InnoDB只支持B-Tree索引(实际是B+Tree变种),因为它能同时支撑等值查询、范围查询(
>、
BETWEEN)、排序(
ORDER BY)和最左前缀匹配。而
Hash索引仅Memory引擎原生支持,且只响应完整等值查询(
WHERE col = ?),不支持
col > 100或
WHERE col1 = ? AND col2 > ?;
FULLTEXT索引专用于
MATCH() AGAINST()全文检索,但对中文需额外分词插件(MySQL原生不支持),且无法用于
WHERE col LIKE '%xx%'这类模糊查询。 别在
datetime字段上建Hash索引——语法会通过,但InnoDB会静默转成B-Tree
FULLTEXT必须建在
CHAR/VARCHAR/TEXT列上,且建表时指定
ENGINE=InnoDB(5.6.4+) 联合索引
(a,b,c)对
WHERE a=1 AND b>10有效,但对
WHERE b>10完全无效
什么时候加索引?看执行计划比猜更可靠
别凭感觉加索引。先用
EXPLAIN看查询是否走了索引:
EXPLAIN SELECT * FROM orders WHERE user_id = 123;。重点看
type字段——
const/
ref表示高效命中,
range尚可,
ALL就是全表扫描。再检查
key列是否显示用了哪个索引,
rows是否远小于表总行数。常见误判点: 单列索引
user_id和联合索引
(user_id, status)共存时,优化器可能选错,用
FORCE INDEX临时干预
OR条件容易让索引失效,比如
WHERE a=1 OR b=2,建议拆成
UNION或补上复合索引
LIKE以通配符开头(
LIKE '%abc')必然无法用B-Tree索引,这是结构限制,不是配置问题
索引不是越多越好:维护成本和写放大必须算进账
每多一个索引,
INSERT就要多写一棵B+Tree叶子节点,
UPDATE改索引列时还要分裂页、合并页。线上表如果QPS高、写入频繁,盲目加索引反而导致
innodb_row_lock_waits飙升。真实案例:某订单表加了5个冗余索引后,日均慢查没降,但磁盘IO等待从2ms涨到18ms。建议做法: 用
information_schema.STATISTICS定期查未被使用的索引:
SELECT * FROM STATISTICS WHERE TABLE_SCHEMA='db' AND INDEX_NAME NOT IN (SELECT DISTINCT INDEX_NAME FROM performance_schema.events_statements_summary_by_digest WHERE DIGEST_TEXT LIKE '%table%');删除索引前先在测试环境开
slow_query_log并设
long_query_time=0,跑一周业务流量,确认无慢查回归 联合索引优先覆盖高频查询组合,宁可少建两个单列索引,也不要建三个互相重叠的联合索引
最常被忽略的一点:索引字段顺序直接影响过滤效率。比如用户登录常用
WHERE email = ? AND status = 'active',那
(email, status)比
(status, email)高效得多——因为status的区分度通常远低于email,放前面会导致B+Tree第一层就过滤不掉多少数据。
