MySQL 索引入门:从 B+树到最左前缀
第一次在 MySQL 里查一个用户的订单,表只有几十万行,查询却等了好几秒。问题不在数据量,而在查询方式:没有索引,数据库只能从第一行翻到最后一行逐行比对,这就是全表扫描。给关键列加上索引后,同样的查询几乎瞬间完成。
一、为什么需要索引
把表想象成一本没有目录的书:找某个用户的订单,只能从第一页翻到最后一页,数据翻倍,时间也翻倍。索引就是这本书的目录,按顺序记录关键列的值和对应行的位置。查询时先查目录,定位到少数几行再取数据,扫描量一下子降了下来。目录本身也要占地方、要维护,所以索引能加速查询,却不是免费的。
二、B+树与聚簇、非聚簇索引
InnoDB 的索引大多用 B+树实现:内部节点只保存键值和指针,真实数据都集中在叶子节点,叶子节点按顺序用链表连起来。树很矮,三四层就能存下上亿行;范围查询也很顺,定位到起点后沿着叶子链表往后翻就行。
聚簇索引按主键组织整张表,叶子节点直接保存完整的行数据,一张表只能有一个聚簇索引,通常就是主键。非聚簇索引也叫二级索引,叶子节点保存索引列的值加上主键值,查到主键后还要回到聚簇索引取整行数据,这多走的一步叫回表。
三、创建与查看索引
CREATE INDEX idx_orders_user_id ON orders(user_id);
SHOW INDEX FROM orders;
EXPLAIN SELECT * FROM orders WHERE user_id = 1001;
动手前先用 EXPLAIN 看查询走了哪条路:type 出现 ref 或 range,一般说明用上了索引;出现 ALL,基本就是全表扫描,要检查条件列上有没有合适的索引。
四、联合索引与最左前缀
同时按用户和时间查订单,可以建联合索引:
CREATE INDEX idx_user_created ON orders(user_id, created_at);
它的规则叫最左前缀原则:必须从最左边的列开始依次使用。这个索引能服务 user_id 的查询,也能服务 user_id 加 created_at 的查询,却服务不了只查 created_at 的查询,因为它跳过了最左边的 user_id。设计时把最常出现在 WHERE 里、用来等值匹配的列放在左边。
五、常见的索引失效场景
对索引列做函数运算会失效:WHERE YEAR(created_at) = 2026 要对每一行都算一遍,改成范围条件 WHERE created_at >= '2026-01-01' AND created_at < '2027-01-01' 就能走索引。模糊查询的前导百分号也会失效:WHERE name LIKE '张%' 可以按前缀定位,而 WHERE name LIKE '%张%' 以百分号开头,只能逐行扫描。隐式类型转换同样如此:phone 列是字符串时,WHERE phone = 13812345678 会把整列转成数字再比较,正确写法是给值加引号。
六、一条忠告:索引不是越多越好
每多建一个索引,写入时就要多维护一棵 B+树,插入、更新和删除都会变慢,磁盘占用也会增加。合理的做法是先看慢查询和 EXPLAIN 输出,只给最常用、区分度高的条件建索引,定期清理长期不用的索引。索引是工具,够用、好用才是目标。
- 点赞
- 收藏
- 关注作者
评论(0)