Carry の Blog Carry の Blog
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • Python
  • Shell
  • MySQL8-SOP手册
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Carry の Blog

好记性不如烂键盘
首页
  • Nginx
  • Prometheus
  • Iptables
  • Systemd
  • Firewalld
  • Docker
  • Sshd
  • DBA工作笔记
  • MySQL
  • Redis
  • TiDB
  • Elasticsearch
  • Python
  • Shell
  • MySQL8-SOP手册
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • MySQL

    • MySQL8一键安装配置优化
    • MySQL导出CSV格式解决乱码
    • MySQL 角色管理
    • MySQL网络抓包审计
    • MySQL性能压测
    • MySQL配合Consul读写分离
    • Gh-ost重建表,清除表碎片率
    • MySQL MGR配合MySQL-router实现innodb-cluster
    • MySQL 快速分析binlog定位问题
    • MySQL执行计划分析
    • DBA常用SQL和命令整理备查
    • mysqldump实时同步数据
    • MySQL的事务隔离级别
    • MySQL存储过程批量生成数据
    • MySQL insert on duplicate key update,replace into , insert ignore的理解
    • MySQL不同字符集之间的区别和选择
    • MySQL为什么有时候会选错索引
      • 可能原因
        • 1. 统计信息不准确
        • 2. 数据分布不均匀
        • 3. 范围条件导致的优化器误判
        • 4. 查询条件顺序与索引不匹配
      • 解决方法
        • 1. 更新统计信息
        • 2. 使用强制索引(谨慎使用)
        • 3. 优化索引设计
        • 4. 使用EXPLAIN分析执行计划
        • 5. 监控和优化
      • 最佳实践
    • MySQL死锁问题
    • MySQL使用SQL语句查重去重
    • MySQLdump逻辑备份
    • MySQL主从跳过异常GITD
    • MySQL8设置slowlog记录所有语句
    • MySQL8快速克隆插件使用指南
    • MySQL8双1设置保障安全
    • MySQL锁
    • innodb cluster安装
    • MySQL里的left join 和right join以及inner join
    • optimize table和 analyze table的区别
    • MySQL 字段的区分度计算公式
    • MySQLReplicaSet 安装
    • 脚本实现MySQL ReplicaSet 高可用
    • MySQL 的Left join,Right join和Inner join 的区别
    • MySQL45讲学习笔记
  • Redis

  • Keydb

  • TiDB

  • MongoDB

  • Elasticsearch

  • Kafka

  • victoriametrics

  • BigData

  • Sqlserver

  • 数据库
  • MySQL
Carry の Blog
2022-03-12
目录

MySQL为什么有时候会选错索引原创

# MySQL为什么有时候会选错索引

在MySQL查询优化中,索引选择是非常关键的一环。有时即使创建了合适的索引,MySQL也可能选择次优的执行计划。本文将详细介绍MySQL选择错误索引的常见原因及其解决方案。

# 可能原因

# 1. 统计信息不准确

MySQL优化器依赖统计信息来评估不同执行计划的成本。统计信息不准确会导致优化器做出错误的选择。

示例: 假设有一个用户表:

CREATE TABLE users (
    id INT PRIMARY KEY,
    status TINYINT,
    created_at DATETIME,
    INDEX idx_status_created (status, created_at)
);
1
2
3
4
5
6

如果表中数据发生大量变化,而统计信息未更新,优化器可能会错误估计 status 字段的选择性,导致在查询时选择了次优索引。

# 2. 数据分布不均匀

当索引列的数据分布极度不均匀时,MySQL的成本估算可能出现偏差。

示例:

SELECT * FROM orders 
WHERE status = 'pending' 
  AND created_at > '2024-01-01';
1
2
3

如果 status 字段中 'pending' 占比非常大(比如90%),而优化器统计信息没有准确反映这一点,可能会错误地选择 status 索引而不是时间索引。

# 3. 范围条件导致的优化器误判

当查询同时包含等值和范围条件时,优化器可能会做出次优选择。

示例:

SELECT * FROM products 
WHERE category_id = 1 
  AND price BETWEEN 100 AND 200;
1
2
3

即使同时建立了 (category_id, price) 和 (price, category_id) 两个索引,优化器可能会因为范围条件的估算误差而选择次优索引。

# 4. 查询条件顺序与索引不匹配

复合索引的列顺序对索引使用效率有重要影响。

示例: 假设有索引:INDEX idx_abc (a, b, c)

-- 能够充分利用索引
SELECT * FROM table WHERE a = 1 AND b = 2 AND c = 3;

-- 可能无法充分利用索引
SELECT * FROM table WHERE b = 2 AND c = 3 AND a = 1;
1
2
3
4
5

虽然两个查询的逻辑相同,但第二个查询可能导致优化器选择次优执行计划。

# 解决方法

# 1. 更新统计信息

定期执行 ANALYZE TABLE 保持统计信息准确:

ANALYZE TABLE table_name;
1

对于 InnoDB 表,也可以调整统计信息采样页面数:

SET GLOBAL innodb_stats_persistent_sample_pages = 20000;
1

# 2. 使用强制索引(谨慎使用)

当确定优化器选择了次优索引时,可以强制使用特定索引:

SELECT * FROM users FORCE INDEX (idx_status_created)
WHERE status = 1 AND created_at > '2024-01-01';
1
2

注意:这应该是临时解决方案,长期应该找出优化器选错索引的根本原因。

# 3. 优化索引设计

根据查询模式调整索引设计:

  1. 考虑列的选择性
  2. 考虑查询条件的顺序
  3. 避免冗余索引

示例:

-- 如果经常按状态+时间查询,但状态的选择性很低
-- 可以调整索引顺序
ALTER TABLE orders DROP INDEX idx_status_created;
CREATE INDEX idx_created_status ON orders (created_at, status);
1
2
3
4

# 4. 使用EXPLAIN分析执行计划

通过EXPLAIN可以查看优化器选择的执行计划:

EXPLAIN FORMAT=JSON SELECT * FROM users 
WHERE status = 1 AND created_at > '2024-01-01';
1
2

特别关注以下信息:

  • possible_keys:可能使用的索引
  • key:实际使用的索引
  • rows:预估扫描行数
  • filtered:满足条件的行数百分比

# 5. 监控和优化

  1. 定期检查慢查询日志,识别性能问题
  2. 使用性能监控工具(如 Performance Schema)跟踪索引使用情况
  3. 在数据量变化较大时及时更新统计信息

# 最佳实践

  1. 在开发环境进行充分的查询测试
  2. 定期检查和更新统计信息
  3. 对关键查询使用EXPLAIN进行分析
  4. 在数据量较大时,考虑使用分区表
  5. 保持MySQL版本更新,新版本通常包含优化器改进

通过以上方法的组合使用,可以有效减少MySQL选错索引的情况,提升查询性能。记住,索引优化是一个持续的过程,需要根据实际应用场景和数据特征不断调整。

#索引优化#查询性能
上次更新: 4/24/2025

← MySQL不同字符集之间的区别和选择 MySQL死锁问题→

最近更新
01
tidb fast ddl
04-04
02
TiDB配置文件调优 原创
04-03
03
如何移除TiDB中的表分区 原创
04-03
更多文章>
Theme by Vdoing
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式