数据库索引:覆盖索引、哈希索引、索引下推。
数据库查询慢,很多时候问题不在 SQL 本身,而在于索引没被“用透”。我们熟悉 B+ 树索引,但真正影响性能的,往往是几个更细的机制:覆盖索引、哈希索引、索引下推。它们分别从“不回表”“等值快”“少回表”三个角度优化查询。下面结合场景聊聊这三者到底怎么回事。
覆盖索引:让查询不用回表
InnoDB 的二级索引叶子节点存的是索引列 + 主键值。如果一条查询需要的列,都能从二级索引里直接拿到,就不需要再根据主键去聚簇索引里取整行数据,这就是覆盖索引。
比如用户表 `users`,有二级索引 `idx_name(name)`。执行:
SELECT id, name FROM users WHERE name = 'Tom';
因为 `idx_name` 的叶子节点已经有 `name` 和主键 `id`,所以这条查询可以只扫索引就返回结果。`EXPLAIN` 的 `Extra` 会显示 `Using index`,这就是覆盖索引生效的标志。
覆盖索引最大的好处是减少随机 I/O。回表意味着一次或多次聚簇索引查找,代价不低。如果高频查询能做成覆盖索引,性能提升会非常明显。实践上,少用 `SELECT *`,把常用查询列放进联合索引,往往比盲目加索引更有效。但也要注意,索引列太多会增大索引体积,写入和维护成本也会上升。
哈希索引:等值查询的闪电战
哈希索引用哈希表组织数据,通过哈希函数定位桶,等值查询平均时间复杂度接近 O(1)。Memory 引擎显式支持哈希索引,InnoDB 则有自适应哈希索引(AHI),它会自动为频繁访问的等值查询建立哈希索引,属于内部优化,不能手动干预。
哈希索引的短板同样明显:它不存储有序键值,所以不支持范围查询、排序,也不支持最左前缀匹配。比如 `WHERE age > 18` 或 `ORDER BY name`,哈希索引基本帮不上忙。另外,哈希冲突严重时,性能会退化。
因此,哈希索引适合等值查询密集、数据量可控的场景,比如缓存表、字典表、会话表。对于 InnoDB,大多数业务还是以 B+ 树为主,自适应哈希索引可以当作额外惊喜,但不要指望它解决所有问题。
索引下推:把过滤条件下推到存储引擎
索引下推(Index Condition Pushdown,ICP)是 MySQL 5.6 引入的优化。在没有 ICP 时,存储引擎根据索引找到记录后,先回表取出完整行,再交给 Server 层过滤 `WHERE` 条件;有了 ICP,存储引擎可以在索引层直接过滤掉不满足条件的记录,减少回表次数。
举个例子,联合索引 `idx_name_age(name, age)`,查询:
SELECT * FROM users WHERE name LIKE '张%' AND age = 20;
`name LIKE '张%'` 能走索引范围扫描,但 `age = 20` 不能用于索引定位。没有 ICP 时,先找出所有姓张的人,回表后再过滤年龄;有 ICP 时,存储引擎在索引里直接判断 `age = 20`,只把符合条件的记录回表。`EXPLAIN` 会显示 `Using index condition`。
ICP 适用于二级索引,能有效降低回表开销,但它不能替代覆盖索引。如果查询本身已经覆盖索引,回表都不需要,ICP 的意义就不大了。
三者如何配合
覆盖索引解决“根本不用回表”,哈希索引解决“等值查询要快”,索引下推解决“回表前尽量多过滤”。它们不是互斥的,而是不同层面的优化手段。
实际工作中,先看查询模式:等值多还是范围多?返回列是否固定?回表次数是否偏高?然后用 `EXPLAIN` 验证,关注 `type`、`key`、`Extra` 里的 `Using index`、`Using index condition`。不要为了优化而堆索引,写性能、存储空间和优化器选择都需要权衡。
索引优化没有银弹,但理解这些机制后,你至少知道该往哪个方向调。覆盖索引、哈希索引、索引下推,本质上都在回答同一个问题:如何让数据库少做无用功。
转载请注明出处,版权归原作者所有。
管理员
黑卡会员



