目 录CONTENT

文章目录

【应用篇】6.聊聊MySQL索引提示

今天,我们结合官方文档与实战经验,和大家分享我对MySQL索引提示的理解与应用心得。切记谨慎使用,搞不好会画蛇添足。大家要结合Explain一起来排查慢SQL和索引选择。

一、索引提示与优化器提示的区别

在我看来,索引提示和优化器提示是MySQL查询优化中两个不同层面的工具。优化器提示更侧重于影响查询执行的全局决策,比如JOIN顺序、算法选择等;而索引提示则更聚焦于表级别的索引选择,能让我直接干预优化器对特定表的索引使用决策。

在实际工作中,可能有两种提示来精准控制查询执行计划。比如下面这个查询,我既通过优化器提示指定了JOIN算法,又通过索引提示指定了表t1的索引:

SELECT /*+ HASH_JOIN(t1,t2) */ * FROM t1 USE INDEX(i1) JOIN t2;

二、索引提示语法与参数解析

基础语法结构

我在写查询时,通常会把索引提示紧跟在表名之后,这样既能清晰表达我的意图,又符合MySQL的语法规范:

tbl_name [[AS] alias] [USE INDEX (idx1,idx2) IGNORE INDEX (idx3)]

三大核心提示类型的应用场景

,这三种索引提示各有其适用场景:

  • USE INDEX:当我发现优化器没有选择我期望的索引时,会用这个提示来限制优化器仅从指定索引中选择。

  • IGNORE INDEX:如果某个索引统计信息不准确导致性能问题,我会用这个提示来强制优化器忽略它。

  • FORCE INDEX:这种情况比较少,通常在我确定表扫描成本极高时才会使用,它的作用类似USE INDEX,但会让优化器尽量避免表扫描。

用FOR子句实现精细化控制

FOR子句来控制索引提示的作用域,这让能更精准地干预查询的不同阶段:

-- 只影响JOIN阶段的索引选择
USE INDEX FOR JOIN (idx_join) 

-- 只影响排序阶段的索引使用
IGNORE INDEX FOR ORDER BY (idx_order)

如果不写FOR子句,索引提示会作用于查询的所有阶段。比如下面这个提示,就相当于同时作用于JOIN、ORDER BY和GROUP BY阶段:

IGNORE INDEX (idx1)

三、实战中遇到的典型场景与解决方案

场景一:强制使用复合索引

有一次,我们发一线一个慢SQL很奇怪,虽然创建了复合索引,但是但优化器却选择了单列索引。于是用USE INDEX提示强制它使用复合索引,性能大大提升

-- 强制使用col1_col2复合索引而非单个索引
SELECT * FROM orders USE INDEX (idx_col1_col2)
WHERE col1 = 'A' AND col2 = 100;

场景二:忽略低效索引

还有一次,发现某个索引的统计信息严重过时,导致优化器做出了错误的选择。用IGNORE INDEX提示解决了这个问题:

-- 忽略统计信息过时的idx_old索引
SELECT * FROM users IGNORE INDEX (idx_old)
WHERE status = 'active' AND created_at > '2025-01-01';

场景三:多阶段索引控制

在处理复杂查询时,会根据不同阶段的需求设置不同的索引提示:

SELECT * FROM sales t1
USE INDEX FOR JOIN (idx_date)       -- JOIN阶段使用日期索引
IGNORE INDEX FOR ORDER BY (idx_id)  -- 排序阶段忽略ID索引
ORDER BY amount DESC;

四、MySQL 8.0索引提示变化的理解

升级到MySQL 8.0后,我注意到索引提示有了重大变化。从8.0.20开始,MySQL引入了新的索引级优化器提示,逐步替代传统的索引提示:

传统索引提示

新优化器提示

我的使用感受

FORCE INDEX

INDEX

功能一样,但新语法更清晰

IGNORE INDEX

NO_INDEX

更容易理解和使用

USE INDEX FOR JOIN

JOIN_INDEX

明确指定作用域,减少误操作

这里提醒大家注意兼容性问题:传统索引提示预计会在未来版本中弃用,而且新优化器提示对DELETE语句的支持更好。所以我建议大家和我一样,逐步过渡到新的提示语法。

五、高级使用技巧与注意事项

1. 主键引用与索引前缀的使用技巧

在引用主键时,用PRIMARY作为索引名:

SELECT * FROM users USE INDEX (PRIMARY) WHERE id = 100;

而在使用索引前缀时,特别注意确保前缀的唯一性:

-- 假设idx_user_是唯一前缀
SELECT * FROM users USE INDEX (idx_user_);

2. 全文搜索中的索引提示处理

在处理全文搜索时,我会特别注意索引提示的不同行为:自然语言模式下所有提示都会被忽略,而布尔模式下只有JOIN阶段的提示有效。

3. 处理提示冲突的经验

根据我的经验,同一表上不能同时使用USE和FORCE INDEX,否则会报错。而在处理多个提示时,MySQL会先应用USE/FORCE INDEX,再应用IGNORE INDEX。另外,重复的索引声明会被合并,所以不用特意去重。

六、我在工作中遵循的最佳实践

  1. 先分析后提示:我每次添加索引提示前,都会先用EXPLAIN查看执行计划,确保提示确实有必要。

  2. 不过度干预:我始终相信优化器在大多数情况下是正确的,只在确有必要时才使用提示。

  3. 注意版本兼容性:在生产环境使用新提示前,我一定会确认MySQL版本支持。

  4. 关注复制环境:在复制环境中使用提示时,我会特别注意不同节点的处理一致性。

  5. 定期复查:当表结构或数据发生变化后,我会重新评估索引提示的有效性。

七、我在多表DELETE中使用索引提示的经验

在使用DELETE语句时,我发现索引提示有特殊规则:单表DELETE不支持索引提示,而多表DELETE需要使用特定的语法。比如下面这个错误示例和正确示例:

-- 错误示例:单表DELETE无法使用索引提示
EXPLAIN DELETE FROM t1 USE INDEX(col2); 

-- 正确示例:多表DELETE语法
EXPLAIN DELETE t1.* FROM t1 USE INDEX(col2)
WHERE col1 BETWEEN 1 AND 100;

看到正确示例的执行计划中索引被正确使用,我就知道我的提示生效了:

*************************** 1. row ***************************
           id: 1
  select_type: DELETE
        table: t1
         type: range
possible_keys: col2
          key: col2
      key_len: 5
         rows: 72
     filtered: 11.11
        Extra: Using where

结语

在我看来,索引提示是MySQL查询优化中非常强大的工具,但也。它就像一把精密的手术刀,需要我们对数据库原理有深入的理解才能用好。我建议大家在使用索引提示时,始终遵循"优化器优先,提示辅助"的原则,把它作为解决特定问题的最后手段。随着MySQL的不断发展,索引提示的功能也在不断完善,我会继续学习和探索,也希望我的经验能对大家有所帮助。

0

评论区