先聊聊我自己的感受。我刚工作那会儿,以为“建索引”就是把热门查询字段挨个加上 index,结果生产环境一张千万级订单表的查询慢到报警,加了索引反而写入变慢、磁盘飙升,最后被架构师拉着看了半天 explain 才明白,索引这东西远不是“加就完了”。MySQL 里的索引,表面看是加快查询的数据结构,往深了说,它直接影响表的物理存储方式、写入吞吐、查询计划,甚至整个数据库的生命周期。这篇文我不打算给你堆概念目录,而是从一条慢查询的真实场景开始,把主键索引、二级索引、联合索引、最左前缀、覆盖索引、索引下推、索引失效这些事,用我能讲到的最直白的方式过一遍,顺便把生产环境踩过的坑一起交代了。
1. 先搞清楚:索引到底帮我们省了什么
1.1 一次慢查询的排查,让我想通了索引的价值
之前维护过一个订单系统,有个表叫 order_info,数据量大概一千万行。某天线上反馈后台订单查询接口超时,我看了一下慢查询日志,发现有一条 SQL 平均耗时三秒多:
sql复制SELECT *
FROM order_info
WHERE user_id = 123456
ORDER BY create_time DESC
LIMIT 10;
这条 SQL 看起来平平无奇,user_id 上也建了索引,怎么会慢?我执行 EXPLAIN 一看,possible_keys 里有 idx_user_id,但 key 却是 NULL,type 显示 ALL,也就是全表扫描。当时我第一反应是“优化器是不是傻了”,后来查了数据分布才发现,user_id = 123456 这个用户的订单量几十万行,占了整表相当比例,优化器算了笔账,觉得走二级索引再回表,还不如全表扫一遍然后排序快。
这就是索引的第一个真相:索引不是越建越快,而是帮你减少数据扫描范围。全表扫描要读一千万行,走了索引但命中数据量太大时,回表成本叠加起来可能更贵,优化器当然会“叛变”。而真正理想的索引设计,是让查询的扫描行数足够少,少到优化器哪怕闭着眼睛也知道该选它。
从这个案例能引出一个特别重要的判断标准:看一个索引建得好不好,不是看 SQL 里“有没有用到索引”,而是看扫描行数下降了几个数量级,以及有没有额外的排序、回表、临时表操作。 如果一条 SQL 扫了 500 万行才出 10 条结果,即便走索引也没意义,本质上还是等于在灾难现场打补丁。
1.2 用翻书目录来理解B+树索引
讲索引原理,很多文章喜欢堆“平衡树”“多路搜索”这些术语,听着很吓人。我更喜欢拿《新华字典》做比喻:字典正文是按拼音排序的,如果你想找一个读音是“suo”的字,有两种办法,第一种从第一页翻到最后一页,第二种先看目录页找到“s”所在的大范围,再顺着“suo”精确锁定页码,然后翻过去。
MySQL 的索引就是那套目录。但它的目录不是简单的“音序 -> 页码”两层结构,而是一棵从根节点到叶子节点的多级 B+ 树。最底层的叶子节点按照索引列的值排好序,上层节点存储“某个范围的最小值/最大值”和对应的子节点指针,方便快速定位。
为什么目录必须“排好序”?因为只有排好序,才能做二分查找。你翻字典找字,不需要从每个字开头看;数据库沿着 B+ 树往下走,每次比较都能砍掉一大半数据,这也是为什么 B+ 树能把千万级数据的查找次数压到三到四次磁盘 IO。所以索引的本质一句话:用额外的有序数据结构,换查询时的快速定位。
这里要先破一个误区,有不少人觉得“索引越多越好”,真的不是。每建一个索引,写入数据时都要额外维护一棵 B+ 树的排序和变更,插入一条记录可能要同时更新好几个索引。如果你的业务是高频写入场景,索引建太多会导致磁盘 IO 和 CPU 开销明显上升,表面上查询快了,整体吞吐却掉了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 为什么InnoDB选B+树当索引结构,而不是红黑树或哈希
2.1 磁盘读数据的物理瓶颈:随机IO有多慢
要理解 B+ 树,得先理解数据库最大的敌人——磁盘随机 IO。一个机械硬盘随机读写一次大概要 5 到 10 毫秒,SSD 在几十微秒到几百微秒量级,听起来不大,但要知道 CPU 处理一条指令是纳秒级的。数据库里动辄几百万行数据,如果每查一个值都要满盘乱转,系统早就卡死了。
所以数据库设计的第一准则,就是尽量减少磁盘访问次数。二叉搜索树(BST)能不能当索引?理论行,但它最差情况下会退化成链表,查找次数等于树高。红黑树是平衡的,树高大概是 log2(N),一千万数据大约 24 层,也就是最多 24 次磁盘 IO,太深了。而且红黑树每个节点只存一个键值,相邻节点的数据在磁盘上往往不连续,这就会导致大量的随机 IO。
B+ 树厉害在哪?它是一个节点里存储多个键值,每个节点对应磁盘上一个页(默认 16KB),树的高度通常只有 3 到 4 层。一千万数据、每个节点能存上百个键时,三层树就能装下,查找时最多读 3 到 4 个页,也就是 3 到 4 次 IO,代价完全可控。
2.2 红黑树、B树、哈希、B+树:谁更适合当索引
很多人分不清 B 树和 B+ 树,简单说:B 树每个节点既有索引键又有数据(或者数据指针),而 B+ 树只有叶子节点存数据,非叶子节点只存键值和子节点指针。这带来了几个关键差异。
- B+ 树的非叶子节点能存更多键,树更矮,磁盘 IO 更少;
- B+ 树的叶子节点之间用双向链表连接,排序、范围查询、
ORDER BY、分页都更友好; - B+ 树的所有叶子节点都在同一层,查询任意数据的耗时非常稳定。
而哈希索引呢?InnoDB 其实有自适应哈希索引,但它是自动维护的,人不能直接创建。哈希索引的优点是等值查询 O(1),可它致命缺点是不支持范围查询、不支持模糊查询、也不支持排序。你在业务里绝大多数查询都不是简单的等值,所以 InnoDB 默认的索引结构必须是 B+ 树。
我整理了一张对比表,方便你直观感受:
| 结构 | 等值查询
