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

MySQL 索引优化实战 – 结合 Explain 深度解析慢查询

在实际的开发过程中,随着数据量的不断增大,慢查询成为了不可忽视的性能瓶颈。MySQL 提供了多种工具来帮助我们分析查询性能问题,其中最常用的工具是 EXPLAINSHOW PROFILESHOW STATUS。本文将从慢查询日志入手,结合 EXPLAIN 解析慢查询,找出索引失效的原因,并提供相应的优化方案。通过本案例,您将掌握如何高效地优化 SQL 查询,提高 MySQL 的查询性能。


一、慢查询日志分析

慢查询日志是 MySQL 记录执行时间超过指定阈值的 SQL 语句的日志,主要用于分析性能瓶颈。要启用慢查询日志,可以通过以下命令设置:

-- 启用慢查询日志
SET GLOBAL slow_query_log = 'ON';

-- 设置慢查询阈值为 2 秒
SET GLOBAL long_query_time = 2;

这样 MySQL 就会将执行超过 2 秒的查询记录到慢查询日志中。接下来,我们将基于慢查询日志中的一个例子进行分析。

假设有以下 SQL 查询是慢查询日志中的一条:

SELECT order_id, user_id, amount, order_date 
FROM orders 
WHERE user_id = 12345 AND order_date BETWEEN '2024-01-01' AND '2024-01-31'
ORDER BY order_date DESC;

二、使用 EXPLAIN 分析 SQL 查询

EXPLAIN 关键字可以帮助我们分析 SQL 执行计划,查看 MySQL 在执行查询时的处理方式,尤其是使用了哪些索引,如何扫描数据等。

1. 基本的 EXPLAIN 输出

通过 EXPLAIN 解析慢查询 SQL:

EXPLAIN SELECT order_id, user_id, amount, order_date 
FROM orders 
WHERE user_id = 12345 AND order_date BETWEEN '2024-01-01' AND '2024-01-31'
ORDER BY order_date DESC;

EXPLAIN 输出结果

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEordersALLidx_user_id, idx_order_dateNULLNULLNULL10000Using where; Using filesort
分析
  1. table:查询涉及的表是 orders
  2. type:扫描类型是 ALL,表示全表扫描。理想情况下,查询应该使用索引,而不是全表扫描。ALL 表示性能较差。
  3. possible_keys:显示 MySQL 可能使用的索引,包括 idx_user_ididx_order_date。这些索引覆盖了查询的字段。
  4. key:显示实际使用的索引,当前没有使用任何索引(NULL)。
  5. Extra:显示额外的信息,当前显示的是 Using where; Using filesort,表示使用了 WHERE 子句过滤数据,并且对查询结果进行了额外的文件排序。

EXPLAIN 的结果来看,MySQL 没有使用任何索引进行查询,而是进行全表扫描并在应用层进行排序。导致查询效率低下。


三、分析索引失效的原因

在上述查询中,尽管存在针对 user_idorder_date 的索引,MySQL 仍然没有使用这些索引。我们可以分析可能的原因:

1. WHERE 子句中有多个条件

WHERE 子句中有两个条件:user_id = 12345order_date BETWEEN '2024-01-01' AND '2024-01-31'。如果这两个条件中的字段没有组成联合索引,MySQL 可能选择不使用索引,导致全表扫描。

2. ORDER BY 子句影响

查询中包含 ORDER BY order_date DESC,这意味着 MySQL 在查询完成后需要对数据进行排序。如果没有合适的索引,MySQL 会使用 filesort,这也是 EXPLAIN 输出中的提示。

3. 索引选择性差

如果 user_idorder_date 字段的选择性较差(即某些值的重复程度较高),MySQL 可能认为索引的选择效率较低,反而选择进行全表扫描。


四、优化方案:

1. 创建联合索引

针对查询的 WHERE 子句和 ORDER BY 子句,我们可以创建一个联合索引,这样 MySQL 就可以同时使用索引来过滤数据和排序。

优化后的索引创建

CREATE INDEX idx_user_id_order_date ON orders(user_id, order_date);

这个索引可以同时支持 WHERE 条件中的 user_idorder_date 字段,并且能够优化 ORDER BY 操作。

2. 查看优化后的执行计划

创建索引后,我们可以再次使用 EXPLAIN 查看优化后的执行计划:

EXPLAIN SELECT order_id, user_id, amount, order_date 
FROM orders 
WHERE user_id = 12345 AND order_date BETWEEN '2024-01-01' AND '2024-01-31'
ORDER BY order_date DESC;

优化后的 EXPLAIN 输出

idselect_typetabletypepossible_keyskeykey_lenrefrowsExtra
1SIMPLEordersrefidx_user_id_order_dateidx_user_id_order_date5const100Using where; Using index
分析
  1. type:现在的类型是 ref,表示使用了索引。refALL 更高效,因为 MySQL 只扫描了相关的索引记录。
  2. key:使用了我们新创建的 idx_user_id_order_date 联合索引。
  3. Extra:显示了 Using where; Using index,意味着查询现在通过索引过滤数据,并且不需要额外的排序操作。

通过创建联合索引,查询性能得到了显著提升,MySQL 能够利用索引进行高效的数据过滤和排序。


五、进一步优化:使用 SHOW PROFILE 和 SHOW STATUS

除了 EXPLAIN,我们还可以使用 SHOW PROFILESHOW STATUS 进一步分析查询的性能瓶颈。

1. 使用 SHOW PROFILE

SHOW PROFILE 可以帮助我们查看 SQL 查询执行过程中的各个阶段,了解每个步骤的时间消耗。

SET PROFILING = 1;  -- 启用 Profiling
SELECT order_id, user_id, amount, order_date 
FROM orders 
WHERE user_id = 12345 AND order_date BETWEEN '2024-01-01' AND '2024-01-31'
ORDER BY order_date DESC;
SHOW PROFILE FOR QUERY 1;  -- 查看第一条查询的执行时间和资源消耗

这将显示每个阶段(如查询解析、排序、读取数据等)的时间消耗,帮助你发现瓶颈所在。

2. 使用 SHOW STATUS

SHOW STATUS 可以帮助我们了解当前数据库的状态,特别是在查询优化方面,我们可以查看相关的缓存命中率、索引使用情况等信息。

SHOW STATUS LIKE 'Handler_read_rnd_next';  -- 全表扫描次数
SHOW STATUS LIKE 'Handler_read_key';      -- 索引扫描次数

通过这些状态信息,我们可以监控查询优化的效果,确保索引被有效利用。


六、总结

  1. EXPLAIN 是优化查询的核心工具,它能帮助我们了解 MySQL 执行查询时的决策,分析索引是否有效、查询是否合理。
  2. 创建合适的索引:为查询条件和排序条件创建联合索引可以大幅提升查询性能,减少全表扫描。
  3. SHOW PROFILE 和 SHOW STATUS:这些工具提供了更详细的执行信息,可以帮助我们进一步识别和优化查询瓶颈。
  4. 索引优化是一项持续的工作,随着数据量的增加,可能需要不断调整索引和查询策略,以应对性能变化。

通过合理使用索引、优化 SQL 语句设计、分析执行计划,能够大幅提升 MySQL 查询性能,尤其是在处理复杂查询时,能有效减少性能瓶颈。


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

相关文章:

  • canvas+fabric实现时间刻度尺+长方形数据展示
  • Vue项目中生成node_modules文件夹的两种常用方法及npm优势
  • 啥是大模型
  • golang 编程规范 - 项目目录结构
  • 高效管理 Nginx 的利器:nginxWebUI 指南和 Docker 部署安装过程
  • 56.在 Vue 3 中使用 OpenLayers 通过 moveend 事件获取地图左上和右下的坐标信息
  • REST与RPC的对比:从性能到扩展性的全面分析
  • 非关系型数据库和关系型数据库的区别
  • 免登录游客卡密发放系统PHP网站源码
  • Excel 面试 01 “Highlight in red the 10 lowest orders”
  • 如何查看下载到本地的大模型的具体大小?占了多少存储空间:Llama-3.1-8B下载到本地大概15GB
  • 系统架构风险、敏感点和权衡点的理解
  • MySQL数据库笔记——主从复制
  • Redis 实战篇 ——《黑马点评》(上)
  • 关于内网服务器依托可上网电脑实现访问互联网
  • MyBatis使用的设计模式
  • 【每日学点鸿蒙知识】输入法按压效果、web组件回弹、H5回退问题、Flex限制两行、密码输入自定义样式
  • GAMS安装和介绍、GAMS程序编写、GAMS程序调试、实际应用算例演示
  • Go语言学习路线
  • 硬件基础知识笔记(2)——二级管、三极管、MOS管
  • Lesson04 类与对象(3补充 友元、内部类)
  • Node 使用pkg打包部署
  • PyTorch中的autocast:混合精度训练的实现原理
  • Llama系列关键知识总结
  • Spring Boot项目中解决跨域问题(四种方式)
  • vue3配置caddy作为静态服务器,在浏览器地址栏刷新出现404