使用explain优化慢查询的业务场景分析

  • 问:你最害怕的事情是什么?
  • 答:搓澡
  • 问:为什么?
  • 答:因为有些人一旦错过,就不在了

Explain 这个词在不同的上下文中有不同的含义。在数据库查询优化的上下文中,“EXPLAIN” 是一个常用的 SQL 命令,用于显示 SQL 查询的执行计划。执行计划是数据库如何执行查询的一个详细描述,包括它将使用哪些索引、表的连接顺序、表的扫描方式等信息。

在 SQL 中,使用 “EXPLAIN” 可以提供以下字段的信息:

  • id: 表示查询中的各个部分的标识符。
  • select_type: 查询类型,比如简单查询、联合查询、子查询等。
  • table: 涉及的表名。
  • partitions: 查询涉及的分区信息。
  • type: 连接类型,如全表扫描、索引扫描等。
  • possible_keys: 可能使用的索引列表。
  • key: 实际使用的索引。
  • key_len: 使用的索引长度。
  • ref: 索引列上使用的列或常量。
  • rows: 估计需要检查的行数。
  • filtered: 行过滤的百分比。
  • Extra: 额外信息,可能包含诸如"Using filesort"、"Using temporary"等信息。

下面,V 哥通过两个案例来详细说明一下如何使用 Explain来优化 SQL。

案例一:

场景设定

假设我们有一个电子商务网站的数据库,其中有一个名为 orders 的表,它记录了用户的订单信息。表结构大致如下:

    id: 订单的唯一标识符
    user_id: 下单用户的ID
    product_id: 购买的产品ID
    order_date: 下单日期
    quantity: 购买数量

问题

我们需要查询2024年1月1日之后所有用户的订单总数。

原始 SQL 查询

SELECT COUNT(*) FROM orders WHERE order_date > '2024-01-01';

步骤 1: 使用 EXPLAIN 分析查询

首先,我们使用 EXPLAIN 来查看当前查询的执行计划:

EXPLAIN SELECT COUNT(*) FROM orders WHERE order_date > '2024-01-01';

步骤 2: 分析 EXPLAIN 输出

假设 EXPLAIN 的输出显示如下:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEordersNULLrangeorder_dateNULLNULLNULL1000010.00Using where; Using index

步骤 3: 识别问题

从 EXPLAIN 输出中,我们可以看到:

  • type 是 range,这意味着数据库将使用索引进行范围扫描,而不是全表扫描。
  • rows 估计为 10000,这可能表示查询需要检查大量行。
  • Extra 显示 Using where; Using index,表示使用了索引。

步骤 4: 优化 SQL

尽管查询已经使用了索引,但我们可能希望进一步优化性能。考虑到我们只需要统计总数,而不是具体的订单数据,我们可以:

  • 使用索引覆盖扫描:如果 order_date 索引包含 id,则可以避免回表查询,直接在索引中完成统计。

优化后的 SQL 可能如下:

SELECT COUNT(*) FROM orders USE INDEX (order_date) WHERE order_date > '2023-01-01';

步骤 5: 再次使用 EXPLAIN

使用优化后的查询再次运行 EXPLAIN:

EXPLAIN SELECT COUNT(*) FROM orders USE INDEX (order_date) WHERE order_date > '2023-01-01';

步骤 6: 分析优化后的输出

假设优化后的 EXPLAIN 输出显示:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEordersNULLindexorder_dateorder_date4NULL1000010.00Using index; Backward index scan

步骤 7: 评估优化效果

  • type 现在是 index,表示使用了索引覆盖扫描。
  • Extra 显示 Using index; Backward index scan,表示查询仅使用了索引,没有回表。

通过这些步骤,我们对原始查询进行了分析和优化,提高了查询效率。在实际应用中,可能需要根据具体的数据库结构和数据分布进行更多的调整和优化。

案例二:

我们考虑一个更复杂的场景,涉及到多表查询和联结。

场景设定

假设我们有一个在线教育平台的数据库,其中有两个表:

1. students 表,存储学生信息:

  • student_id: 学生ID
  • name: 学生姓名
  • enrollment_date: 入学日期

2. courses 表,存储课程信息:

  • course_id: 课程ID
  • course_name: 课程名称

3. 还有一个 enrollments 表,存储学生的课程注册信息:

  • enrollment_id: 注册ID
  • student_id: 学生ID
  • course_id: 课程ID
  • enrollment_date: 注册日期

问题

我们需要查询所有在2024年注册了至少一门课程的学生的姓名和他们注册的课程数量。

原始 SQL 查询

SELECT s.name, COUNT(e.course_id) AS course_count
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
GROUP BY s.name;

步骤 1: 使用 EXPLAIN 分析查询

EXPLAIN SELECT s.name, COUNT(e.course_id) AS course_count
FROM students s
JOIN enrollments e ON s.student_id = e.student_id
GROUP BY s.name;

步骤 2: 分析 EXPLAIN 输出

假设 EXPLAIN 的输出如下:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1SIMPLEsNULLALLNULLNULLNULLNULL1000NULLNULL
1SIMPLEeNULLrefstudent_idstudent_id5students.student_id5000NULLUsing where

步骤 3: 识别问题

  • students 表使用了全表扫描(type 是 ALL),这意味着查询需要扫描整个 students 表。
  • enrollments 表使用了 ref 类型的联结,它使用了 student_id 索引。

步骤 4: 优化 SQL

我们可以通过以下方式优化查询:

  • 添加索引:如果 enrollments 表上的 enrollment_date 没有索引,考虑添加一个,以便快速过滤2023年的注册记录。
  • 过滤条件:在联结条件中添加过滤条件,减少需要联结的行数。

优化后的 SQL 可能如下:

SELECT s.name, COUNT(e.course_id) AS course_count
FROM students s
JOIN (
  SELECT course_id, student_id
  FROM enrollments
  WHERE enrollment_date >= '2023-01-01'
) e ON s.student_id = e.student_id
GROUP BY s.name;

步骤 5: 再次使用 EXPLAIN

使用优化后的查询再次运行 EXPLAIN。

步骤 6: 分析优化后的输出

假设优化后的 EXPLAIN 输出显示:

idselect_typetablepartitionstypepossible_keyskeykey_lenrefrowsfilteredExtra
1PRIMARYsNULLALLNULLNULLNULLNULL1000NULLNULL
2DERIVEDeNULLrangeenrollment_dateNULLNULLNULL50010.00Using where
1SIMPLE<subquery2>NULLrefstudent_idstudent_id5s.student_id500NULLUsing index

步骤 7: 评估优化效果

  • 子查询 e 现在使用 range 类型扫描,只获取2023年的注册记录,减少了行数。
  • 主查询现在使用 ref 类型联结,因为子查询结果已经通过索引 student_id 进行了优化。

通过这些步骤,我们对原始查询进行了分析和优化,减少了需要处理的数据量,提高了查询效率。在实际应用中,可能需要根据具体的数据库结构和数据分布进行更多的调整和优化。

最后

以上是 V 哥在整理的关于 EXPLAIN 在实际工作中的使用,并结合案例给大家作了分析,用熟 EXPLAIN 将大大改善你的 SQL 查询效率,你在工作中还用到哪些业务场景或案例,可以在评论区讨论,或者说出你遇到的问题,V 哥来帮你定位一下问题,关注威哥爱编程,每天精彩内容不错过。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/762545.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于PHP的初中数学题库管理系统

有需要请加文章底部Q哦 可远程调试 基于PHP的初中数学题库管理系统 一 介绍 此初中数学题库管理系统基于原生PHP开发&#xff0c;数据库mysql&#xff0c;系统角色分为学生&#xff0c;教师和管理员。(附带参考设计文档) 技术栈&#xff1a;phpmysqlphpstudyvscode 二 功能 …

YOLOv10改进教程|C2f-CIB加入注意力机制

一、 导读 论文链接&#xff1a;https://arxiv.org/abs/2311.11587 代码链接&#xff1a;GitHub - CV-ZhangXin/AKConv YOLOv10训练、验证及推理教程 二、 C2f-CIB加入注意力机制 2.1 复制代码 打开ultralytics->nn->modules->block.py文件&#xff0c;复制SE注意力机…

Android 大话binder通信

戳蓝字“牛晓伟”关注我哦&#xff01; 用心坚持输出易读、有趣、有深度、高质量、体系化的技术文章 由于 Android 大话binder通信(上) 和 Android 大话binder通信(下) 分为两篇阅读体验不好&#xff0c;顾合并为一篇。 本文摘要 用故事的方式把binder通信的整个过程都描述…

分享一个在 WinForm 桌面程序中使用进度条展示报表处理进度的例子,提升用户体验

前言 在有些比较消耗时间的业务场景中&#xff0c;比如生成报表等&#xff0c;如果没有在操作的过程中向用户反馈操作进度&#xff0c;会让用户以为程序 “死” 掉了&#xff0c;用户体验非常不好。 WinForm 桌面程序项目与 Console 项目不一样&#xff0c;如果 Console 项目…

C++ initializer_list类型推导

目录 initializer_list C自动类型推断 auto typeid decltype initializer_list<T> C支持统一初始化{ }&#xff0c;出现了一个新的类型initializer_list<T>&#xff0c;一切类型都可以用列表初始化。提供了一种更加灵活、安全和明确的方式来初始化对象。 class…

MIT6.s081 2021 Lab Page tables

Speed up system calls 思路 题目要求在每个进程初始化时为它的页表插入一个页表项&#xff0c;内核通过这样预先缓存页表项的操作&#xff0c;来加速特定系统调用的执行速度。 由于前不久刚过完一遍《OSTEP》&#xff0c;因此我认为自己对页表机制还算比较熟悉&#xff0c;…

Open AI Stream Completion Set Variable Inside Function PHP With Openai-php SDK

题意&#xff1a;使用 OpenAI 的 PHP SDK&#xff08;例如 openai-php&#xff09;来在函数内部设置和完成一个流&#xff08;stream&#xff09;相关的变量 问题背景&#xff1a; How to set variable inside this openai-php sdk function in stream completion ? I am usi…

【笔记】手工部署之linux中开放已安装的mysql与tomcat端口

在需要打包的springboot项目中输入mvn clean package 在target下面获得jar包 进入linux中你想要该jar包存在的位置 将jar包上传至linux中 此时在浏览器中输入linux的ip地址&#xff1a;端口号/mapping路径为404 故&#xff1a; 在linux中另开一个标签页 检查mysql和tomcat已…

JavaFX布局-BorderPane

JavaFX布局-BorderPane 实现方式Java实现FXML实现 综合案例 将容器空间分成五个区域&#xff1a;顶部&#xff08;Top&#xff09;、底部&#xff08;Bottom&#xff09;、左侧&#xff08;Left&#xff09;、右侧&#xff08;Right&#xff09;和中心&#xff08;Center&#…

Java案例找素数(三种方法)

目录 一&#xff1a;问题&#xff1a; 二&#xff1a;思路分析&#xff1a; 三&#xff1a;具体代码&#xff1a; 四&#xff1a;运行结果&#xff1a; 一&#xff1a;问题&#xff1a; 二&#xff1a;思路分析&#xff1a; 三&#xff1a;具体代码&#xff1a; Ⅰ&#xf…

03 _ 类型基础(2):动态类型与静态类型

静态类型语言与动态类型语言 通俗定义 静态类型语言&#xff1a;在编译 阶段确定所有变量的类型 动态类型语言&#xff1a;在执行阶段确定所有变量的类型 Javascript 与 C 对比 静态类型与动态类型对比 其他定义 强类型语言&#xff1a;不允许程序在发生错误后继续执行 语…

【STM32】温湿度采集与OLED显示

一、任务要求 1. 学习I2C总线通信协议&#xff0c;使用STM32F103完成基于I2C协议的AHT20温湿度传感器的数据采集&#xff0c;并将采集的温度-湿度值通过串口输出。 任务要求&#xff1a; 1&#xff09;解释什么是“软件I2C”和“硬件I2C”&#xff1f;&#xff08;阅读野火配…

视频号视频怎么下载保存到手机,视频号视频如何下载到电脑本地

在数字化浪潮的推动下&#xff0c;视频号成为了我们获取信息、分享生活的重要平台。但有时候&#xff0c;我们遇到一些精彩的内容&#xff0c;想要保存下来以便日后观看&#xff0c;却发现视频号并不提供直接的下载功能。下面我就来为大家详细介绍视频号视频下载的方法&#xf…

Datax快速使用之牛刀小试

前言 一次我发现业务他们在用 datax数据同步工具&#xff0c;我尤记得曾经 19 年使用过&#xff0c;并且基于当时的版本还修复了个 BUG并且做了数据同步管道的集成开发。没想到时间过的飞快&#xff0c;业务方基于海豚调度 2.0.6 的版本中有在使用&#xff0c;由于业务方还没有…

大促前夕即高点,综合电商平台的“稀缺”魔法正在消失?

新一期618大促早已结束良久了&#xff0c;但似乎其产生的余韵却仍旧未消散。 从最直观的资本市场走势来看&#xff0c;自这一波618大促陆续开展之后&#xff0c;包括京东、阿里巴巴、拼多多等港美股股价就一改此前的上行态势&#xff0c;持续下滑至今。 事实上&#xff0c;早…

【计算机网络期末复习】例题汇总(一)

重点例题选择填空简答题与传输媒体的接口的特性重点 计算机网络的性能指标计算机网络体系结构例题 选择

【Linux】线程id与互斥(线程三)

上一期我们进行了线程控制的了解与相关操作&#xff0c;但是仍旧有一些问题没有解决 本章第一阶段就是解决tid的问题&#xff0c;第二阶段是进行模拟一个简易线程库&#xff08;为了加深对于C库封装linux原生线程的理解&#xff09;&#xff0c;第三阶段就是互斥。 目录 线程id…

【解锁未来:深入了解机器学习的核心技术与实际应用】

解锁未来&#xff1a;深入了解机器学习的核心技术与实际应用 &#x1f48e;1.引言&#x1f48e;1.1 什么是机器学习&#xff1f; &#x1f48e;2 机器学习的分类&#x1f48e;3 常用的机器学习算法&#x1f48e;3.1 线性回归&#xff08;Linear Regression&#xff09;&#x1…

【PYG】Planetoid中边存储的格式,为什么打印前十条边用edge_index[:, :10]

edge_index 是 PyTorch Geometric 中常用的表示图边的张量。它通常是一个形状为 [2, num_edges] 的二维张量&#xff0c;其中 num_edges 表示图中边的数量。每一列表示一条边&#xff0c;包含两个节点的索引。 实际上这是COO存储格式&#xff0c;官方文档里也有写&#xff0c;…