当前位置: 首页 > article >正文

MySQL 优化详解:从基础到高级全面指南

MySQL 优化是一个系统工程,涵盖查询优化、索引设计、配置调优、架构优化等多个层面。以下是详细的优化策略和方法:

一、SQL 语句优化

1. 查询优化基础

  • 避免 SELECT *:只查询需要的列
-- 不推荐
SELECT * FROM users;

-- 推荐
SELECT id, name, email FROM users;
  • 使用 LIMIT 分页:大数据量时分页优化
-- 低效
SELECT * FROM articles LIMIT 10000, 20;

-- 高效 (使用索引列)
SELECT * FROM articles WHERE id > 10000 LIMIT 20;

2. JOIN 优化

  • 确保关联字段有索引
  • 小表驱动大表原则
-- 假设 departments 是小表
SELECT * FROM departments d 
JOIN employees e ON d.id = e.dept_id;

3. 子查询优化

  • 将子查询转为 JOIN
-- 不推荐
SELECT * FROM products 
WHERE category_id IN (SELECT id FROM categories WHERE type = 'electronics');

-- 推荐
SELECT p.* FROM products p
JOIN categories c ON p.category_id = c.id 
WHERE c.type = 'electronics';

二、索引优化

1. 索引设计原则

  • 最左前缀原则:联合索引 (a,b,c) 可支持 WHERE a=?、WHERE a=? AND b=? 等条件
  • 区分度高的列建索引(如用户ID而非性别)

索引是MySQL性能优化的核心环节,合理的索引设计可以提升查询效率几个数量级。以下从索引原理、设计策略、优化技巧到实战案例进行全面解析。

一、索引底层原理

1. B+Tree索引结构

  • 存储方式:平衡多路搜索树,InnoDB中每个索引都是一棵B+Tree
  • 节点组成
    • 非叶子节点:存储键值和指向子节点的指针
    • 叶子节点:存储键值和数据行指针(聚簇索引存储完整数据行)
  • 特点
    • 所有叶子节点通过指针相连形成链表
    • 非叶子节点不存储数据,只存储索引
    • 通常3-4层即可存储千万级数据

2. 聚簇索引 vs 二级索引

特性

聚簇索引

二级索引

存储内容

完整数据行

索引列+主键值

数量限制

每表1个

每表多个

查询效率

直接获取数据

需要回表

示例

PRIMARY KEY

UNIQUE/普通索引

二、索引类型详解

1. 普通索引

CREATE INDEX idx_name ON users(name);

  • 最基本的索引类型
  • 允许重复值和NULL值

2. 唯一索引

CREATE UNIQUE INDEX idx_email ON users(email);

  • 保证列值的唯一性
  • 允许NULL值(但只能有一个NULL)

3. 主键索引

ALTER TABLE users ADD PRIMARY KEY (id);

  • 特殊的唯一索引
  • 不允许NULL值
  • 自动创建聚簇索引(InnoDB)

4. 联合索引

CREATE INDEX idx_name_age ON users(name, age);

  • 多列组合索引
  • 遵循最左前缀原则

5. 全文索引

ALTER TABLE articles ADD FULLTEXT INDEX ft_content (content);

  • 专用于文本搜索
  • 支持MATCH AGAINST语法

6. 空间索引

CREATE SPATIAL INDEX sp_location ON maps(coordinates);

  • 用于地理空间数据
  • 支持GIS函数计算

三、索引设计策略

1. 索引选择原则

  • 高选择性:区分度=不重复值/总行数 > 10%
-- 计算列的选择性
SELECT COUNT(DISTINCT status)/COUNT(*) FROM orders; -- 区分度低
SELECT COUNT(DISTINCT user_id)/COUNT(*) FROM orders; -- 区分度高
  • 高频查询:WHERE/JOIN/ORDER BY/GROUP BY涉及的列
  • 短字段优先:整型效率 > 字符串

2. 联合索引设计

  • 最左前缀原则INDEX(a,b,c) 支持 a=?a=? AND b=?a=? AND b=? AND c=?,但不支持 b=?
  • 列顺序策略
    1. 区分度高的列在前
    2. 等值查询列在前,范围查询列在后
    3. 常用列在前

示例

-- 良好设计
CREATE INDEX idx_region_age_gender ON users(region, age, gender);

-- 可优化为
CREATE INDEX idx_region_gender_age ON users(region, gender, age);
-- 理由:gender的等值查询通常比age的范围查询更常用

3. 覆盖索引优化

  • 定义:索引包含查询需要的所有字段,避免回表
  • 实现方法
-- 原始查询(需要回表)
SELECT id, name, age FROM users WHERE name LIKE '张%';

-- 优化为覆盖索引
CREATE INDEX idx_name_covering ON users(name, age);
-- 现在索引包含所有查询字段

四、索引失效场景分析

1. 函数操作导致失效

-- 失效(对列使用函数)
SELECT * FROM users WHERE DATE_FORMAT(create_time,'%Y-%m') = '2023-01';

-- 优化方案
SELECT * FROM users WHERE create_time BETWEEN '2023-01-01' AND '2023-01-31';

2. 隐式类型转换

-- user_id是INT类型,但传入字符串(导致索引失效)
SELECT * FROM users WHERE user_id = '1001';

-- 应改为
SELECT * FROM users WHERE user_id = 1001;

3. 模糊查询通配符开头

-- 无法使用索引
SELECT * FROM users WHERE name LIKE '%张';

-- 可以使用索引
SELECT * FROM users WHERE name LIKE '张%';

4. OR条件部分无索引

-- 只有name有索引,age无索引(全表扫描)
SELECT * FROM users WHERE name = '张三' OR age = 30;

-- 优化方案1:为age添加索引
-- 优化方案2:改写为UNION
SELECT * FROM users WHERE name = '张三'
UNION
SELECT * FROM users WHERE age = 30;

5. 使用!=或NOT IN

-- 通常无法使用索引
SELECT * FROM users WHERE status != 1;
SELECT * FROM orders WHERE id NOT IN (1001, 1002);

-- 优化方案:考虑业务逻辑改写
SELECT * FROM users WHERE status IN (0, 2, 3);

更多索引失效的场景:

https://zhuanlan.zhihu.com/p/22362255750

  • 常见原因: 使用 OR、函数、范围查询、LIKE 开头、NOTIS NULLDISTINCT 等。
  • 避免策略: 尽量避免在查询条件中使用这些情况,或者使用合适的索引(如组合索引、覆盖索引)来优化查询。

五、索引优化实战案例

案例1:分页查询优化

问题SQL

SELECT * FROM orders ORDER BY id LIMIT 100000, 10;

优化方案

-- 方案1:使用索引覆盖+延迟关联
SELECT * FROM orders o
JOIN (SELECT id FROM orders ORDER BY id LIMIT 100000, 10) t
ON o.id = t.id;

-- 方案2:记录上次查询的最大ID
SELECT * FROM orders WHERE id > 100000 ORDER BY id LIMIT 10;

案例2:订单统计优化

问题SQL

SELECT user_id, COUNT(*) 
FROM orders 
WHERE create_time > '2023-01-01'
GROUP BY user_id;

优化方案

-- 创建联合索引
CREATE INDEX idx_user_create ON orders(user_id, create_time);

-- 使用覆盖索引
SELECT user_id, COUNT(*) 
FROM orders 
WHERE create_time > '2023-01-01'
GROUP BY user_id;
-- 执行计划应显示"Using index"

六、索引维护与管理

1. 索引监控

-- 查看索引使用情况
SELECT * FROM sys.schema_index_statistics 
WHERE table_schema = 'your_db';

-- 查看冗余索引
SELECT * FROM sys.schema_redundant_indexes;

2. 索引碎片整理

-- 查看碎片率
SELECT table_name, index_name, 
       ROUND(stat_value * @@innodb_page_size / 1024 / 1024, 2) size_mb,
       stat_description
FROM mysql.innodb_index_stats 
WHERE stat_name = 'size' AND database_name = 'your_db';

-- 重建索引
ALTER TABLE orders ENGINE=InnoDB;
-- 或
OPTIMIZE TABLE orders;

3. 索引生命周期管理

  1. 新建索引:上线前在测试环境验证效果
  2. 监控调整:通过性能监控评估索引效果
  3. 下线清理:定期清理使用率低的冗余索引

七、高级索引技术(MySQL 8.0+)

1. 降序索引

CREATE INDEX idx_desc ON orders(create_time DESC, amount ASC);
-- 优化 ORDER BY create_time DESC, amount ASC

2. 函数索引

-- 创建基于函数的索引
CREATE INDEX idx_name_lower ON users((LOWER(name)));

-- 查询使用
SELECT * FROM users WHERE LOWER(name) = '张三';

3. 隐藏索引

-- 测试删除索引影响
ALTER TABLE users ALTER INDEX idx_name INVISIBLE;

-- 恢复可见
ALTER TABLE users ALTER INDEX idx_name VISIBLE;

索引优化检查清单

  1. 所有高频查询条件都有合适索引
  2. 联合索引列顺序遵循最左前缀原则
  3. 避免出现索引失效场景
  4. 大表查询尽量使用覆盖索引
  5. 定期监控和清理冗余索引
  6. 为排序和分组操作建立适当索引

通过深入理解索引原理并结合实际业务场景进行优化,可以显著提升MySQL查询性能。建议结合EXPLAIN分析工具持续优化索引策略。

使用Explain查询sql执行过程进一步优化

参见下一篇博客



 


http://www.kler.cn/a/612486.html

相关文章:

  • 2024年VR市场回顾与2025年AR + AI智能眼镜展望
  • Android 13深度定制:揭秘类MIUI全面屏手势返回动效的架构级实现
  • C# 固高板卡(总线型) 操作类
  • Go 语言规范学习(2)
  • 在shell脚本内部获取该脚本所在目录的绝对路径
  • dbeaver连接mongodb 插入日期变成了字符串
  • Kotlin when 表达式完全指南:从基础到高级的12种实战用法
  • 03 相机标定图像采集
  • ESLint报错:Could not find config file.
  • Redis:String 类型 内部实现、编码、命令及应用场景
  • ESP32-C3物联网方案,智能设备创新升级,无线交互控制通信应用
  • Rk3568驱动开发_设备树点亮LED_10
  • 【机器学习】基础知识
  • 数据库基础知识点(系列三)
  • deepseek+在线markdown生成pdf文件
  • Spring Boot 项目常见漏洞与安全最佳实践
  • Maven工具学习使用(五)——生命周期和插件
  • 【基础】Windows 中通过 VSCode 使用 GCC 编译调试 C++
  • 【深度学习】Cross-Attention(交叉注意力)机制详解与应用
  • python将整个txt文件写入excel的一个单元格?