Mysql 调优记: INNER JOIN查询 Using temporary; Using filesort 问题优化

2023-10-27

近期笔者在生产环境中发现一条执行非常慢的sql。大概时间为5s左右,于是乎对改SQL场景进行EXPLAIN 分析,发现一个在执行过程中出现对“Using temporary; Using filesort ”。即在执行过程中产生了临时表来存储结果,并在排序时根据连接类型以及存储排序键值和匹配条件的全部行的行指针来排序。经过合理对优化,最终EXPLAIN后,没有再发现明细对SQL性能瓶颈,并且SQL执行时间在1s以内。遂将这点经验记录下来。

先看看SQL和大致的表结构和数据量:

订单表 order 表:大概有15w数据

CREATE TABLE `order` (
  `id` char(32) NOT NULL COMMENT 'id',
  `order_status` varchar(5) NOT NULL DEFAULT '0' COMMENT '订单状态',
  `order_time` datetime DEFAULT NULL COMMENT '下单时间',
   PRIMARY KEY (`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单数据';

与订单表关联的付款表:大概11w数据

CREATE TABLE `order_payment` (
  `id` char(32) NOT NULL COMMENT 'id',
  `order_id` char(32) NOT NULL DEFAULT '' COMMENT '订单id',
  `money` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '付款金额',
 PRIMARY KEY (`id`),
 KEY `i_order_id` (`order_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单付款数据';

大概了解表结构后,来看一个很普通sql查询场景,查询所有运单状态未完成的订单,并获取其订单ID和付款金额,并按下单时间倒序。

SQL 很简单:

select o.id, op.money from `order` o 
inner join order_payment op on o.id = op.order_id 
where o.order_status = '4' order by o.order_time DESC;

然后通过EXPLAIN 分析,发现:

select_type table type possible_key key rows Extra
SIMPLE op ALL i_order_id NULL 110000 Using temporary; Using filesort
SIMPLE o eq_ref PRIMARY PRIMARY 150000 Using where

对于上述EXPLAIN的各列含义本文不在多赘述,有兴趣的可以查阅笔者的其他博文有介绍。

首先,了解Using temporary; Using filesort 到底是什么,为什么会成为性能杀手。

Using temporary

表示由于排序没有走索引、使用union、子查询连接查询、使用某些视图等原因,因此创建了一个内部临时表。注意这里的临时表可能是内存上的临时表,也有可能是硬盘上的临时表,理所当然基于内存的临时表的时间消耗肯定要比基于硬盘的临时表的实际消耗小。
当mysql需要创建临时表时,选择内存临时表还是硬盘临时表取决于参数tmp_table_size和max_heap_table_size,内存临时表的最大容量为tmp_table_size和max_heap_table_size值的最小值,当所需临时表的容量大于两者的最小值时,mysql就会使用硬盘临时表存放数据。

无论是基于内存,还是基于硬盘。构建临时表的过程是比较耗时的一个操作

Using filesort

Using filesort的含义不能望字生意,它并不代表使用文件排序。仅仅表示没有使用索引的排序。
filesort使用的算法是QuickSort,即对需要排序的记录生成元数据进行分块排序,然后再使用mergesort方法合并块。其中filesort可以使用的内存空间大小为参数sort_buffer_size的值,默认为2M。当排序记录太多sort_buffer_size不够用时,mysql会使用临时文件来存放各个分块,然后各个分块排序后再多次合并分块最终全局完成排序。
笔者个人的理解是,Using filesort并不一定是性能杀手。因为要优化掉Using filesort的唯一办法就是让排序走索引字段,但这情况要根据实际情况来取舍,毕竟建立索引也是有代价的。

了解了上述两个问题产生的原因后,回看之前的sql为和为出现这两个问题。

Using filesort就不用说了,没有走索引导致的。重点来看Using temporary。为何会产生临时表。

MYSQL优化器:JOIN中的顺序选择

Mysql在遇到inner join联接语句时,MySQL表关联的算法是 Nest Loop Join(嵌套联接循环),Nest Loop Join就是通过两层循环手段进行依次的匹配操作,最后返回结果集合。SQL语句只是描述出希望连接的对象和规则,而执行计划和执行操作要切实际将一行行的记录进行匹配。Nest Loop Join的操作过程很简单,很像我们最简单的排序检索算法,两层循环结构。进行连接的两个数据集合(数据表)分别称为外侧表(驱动表)和内侧表(非驱动表)。Mysql又会怎样去确定,哪张表是驱动表,哪张表又是非非驱动表呢?mysql它以表中数据最小的一张表作为驱动表(也就是基表),而另一张表就叫做非驱动表,首先处理驱动表中每一行符合条件的数据,之后的每一行数据和非驱动表进行连接匹配操作,直到循环结束,最后合并结果、返回结果给用户。对于驱动表的字段它是可以直接排序的,然而对于非驱动表的字段排序需要通过循环查询的合并结果(临时表)进行排序,因此,order by o.order_time 时,就先产生了 using temporary(使用临时表)。

前面我们知道 order_payment 的数据量只有11w,那么理所当然的order_payment是驱动表。所以,为了避免 using temporary,就必须使用order作为驱动表,这个时候STRAIGHT_JOIN关键字就来了。

STRAIGHT_JOIN 强指定驱动表

explain select o.id, op.money from `order` o straight_join order_payment op on o.id = op.order_id  where o.order_status = '4' order by o.order_time DESC;

分析结果,你会发现Using temporary 已经消失了。

最好解决掉Using filesort就很简单了,只需要给order_time增加索引就好了。

但是注意,在这个查询场景下,不能只对order_time一个字段增加索引,这样是避免不了 Using filesort的。因为innerDB索引的特性,B TREE 树的叶子节点存储的是索引列数据和主键ID。当只有一个order_time索引时,因为还有查询条件order_status在,所以无法使用到该索引

最佳索引方案

create index i_order_status_time on `order`(`order_status`,`order_time`)

这样B TREE树的叶子节点存储的会是索引列数据包含(order_status和order_time)和主键ID。该索引既能排序又能走查询。在上述条件查询中做到最佳性能。

最终SQL分析:

select_type table type possible_key key rows Extra
SIMPLE o ref PRIMARY,i_order_status_time i_order_status_time 150000 Using where; Using index
SIMPLE op ref i_order_id i_order_id 110000

查询时候大幅提升。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Mysql 调优记: INNER JOIN查询 Using temporary; Using filesort 问题优化 的相关文章

  • 在一个后台为MYSQL的网站上集成搜索

    我有一个位置搜索website http www jammulinks com对于一个城市 我们首先收集该城市所有可能类别的数据 如学校 学院 百货商店等 并将其信息存储在单独的表中 因为每个条目除了名称 地址和电话号码外都有不同的详细信息
  • Laravel 5.4 升级 - 违反完整性约束 - 列不能为空

    奇怪的是 所有这些都在 5 2 中工作 但我不知道可以改变什么来实现这一点 下面是错误和正在插入的数组 SQLSTATE 23000 Integrity constraint violation 1048 Column gender can
  • 如何将行变成列?

    我有一个数据库 其中存储分组到项目中的关键字以及与每个关键字相关的数据 然后我显示每个项目的数据网格 每个关键字一行和几列 全部从同一个表 数据 中检索 我有 4 个表 关键字 项目 group keywords 和数据 keywords
  • PHP使用auto_increment生成短唯一ID?

    我想生成一个简短的 唯一的 ID 而不必检查冲突 我目前正在做类似的事情 但是我当前生成的 ID 是随机的 并且在循环中检查冲突很烦人 并且如果记录数量显着增加 将会变得昂贵 通常担心冲突不是问题 但我想要生成的唯一 ID 是一个由 5 8
  • MySQL集群启动失败

    这不是我第一次创建ndbcluster 但我没有收到这样的问题 我正在关注本手册 https hub docker com r mysql mysql cluster by mysql团队 我正在使用回显的默认配置在此 GitHub 存储库
  • MySQL - 多个结果集

    我正在使用 NET Connector 连接到 MySQL 在我的应用程序中 很少有线程使用相同的连接 因此如果 MySQLDataReader 尚未关闭并且某个线程正在尝试执行查询 则会出现该错误 已经有一个打开的 DataReader
  • AWS RDS MySql - 如何在设置“公开可用”后允许访问

    刚刚使用默认设置和用户 密码创建了新的 AWS RDS MySql 实例 我也将其设置为publicly available并在此过程中创建新的 VPC 目前无法从我的笔记本电脑连接到此 RDS mysql h endpoint u myu
  • mysql表中的数据非常大。即使 select 语句也需要很多时间

    我正在开发一个数据库 它是一个相当大的数据库 有 13 亿行和大约 35 列 这是我检查表状态后得到的结果 Name Table Name Engine InnoDB Version 10 Row format Compact Rows 1
  • mysql-connector-c++ - “get_driver_instance”不是“sql::mysql”的成员

    我是 C 的初学者 我认为学习的唯一方法就是接触一些代码 我正在尝试构建一个连接到 mysql 数据库的程序 我在 Linux 上使用 g 没有想法 我运行 make 这是我的错误 hello cpp 38 error get driver
  • 从 call_log 中获取最大并发通话数

    我需要帮助在 MySQL 5 0 77 中编写一个查询 根据下面所示的数据 办公室一天的通话量 返回并发电话呼叫的峰值数量 我只是想知道一天中任何特定时间同时打电话的人数最多是多少 首先 这是 MySQL 表 CREATE TABLE ca
  • 没有提示指令的直连接中表的顺序是否会影响性能?

    所有基于 SQL 的 RDBMS 10 年前的版本 直接连接查询 没有提示指令 中的表顺序是否会对最佳性能和内存管理产生影响 听说最后一个join应该是最大的表 您的数据库的查询优化器如何处理这种情况 回答你的问题 是的 表的顺序在连接中有
  • jdbc4.MySQLSyntaxErrorException:数据库中不存在表

    我正在使用 SpringBoot 开发一个网络应用程序 这是我的application properties文件来指定访问数据库的凭据 spring datasource driverClassName com mysql jdbc Dri
  • Tomcat 6找不到mysql驱动

    这里有一个类似的问题 但关于类路径 ClassNotFoundException com mysql jdbc Driver https stackoverflow com questions 1585811 classnotfoundex
  • 使用 MYSQL 将 h:mm pm/am 时间格式插入数据库

    我正在尝试将以 h mm am pm 格式写入的时间插入到存储为标准 DATETIME 格式 hh mm ss 的数据库中 但我不知道如何将发布的时间转换为标准格式所以数据库会接受它 这是我到目前为止一直在尝试的 title POST in
  • mysql 不带空字符串和 NULL 的不同值

    如何检索没有空字符串值和NULL值的mysql不同值 SELECT DISTINCT CON EMAILADDRESS AS E MAIL FROM STUDENT INNER JOIN CONTACT CON ON STUDENT CON
  • 使用什么框架来引导我的第一个生产 scala 项目?

    我正在第一次涉足 scala 的生产应用程序 该应用程序当前打包为 war 文件 我的计划是创建 scala 编译工件的 jar 文件 并将其添加到 war 文件的 lib 文件夹中 我的增强功能是通过 Jersey 公开的 mysql 支
  • mysql 如何将 varchar(10) 转换为 TIMESTAMP?

    我已将所有日期存储到数据库中varchar 10 现在我想将它们转换为 TIMESTAMP 当我运行sql时 ALTER TABLE demo3 CHANGE date date TIMESTAMP NOT NULL 它提醒 1292 In
  • 如何使用 MySQL 选择有特定值的 2 个连续行?

    我正在构建一个系统 该系统应该显示学生何时连续缺席两天 例如 此表包含缺勤情况 day id missed 2016 10 6 1 true 2016 10 6 2 true 2016 10 6 3 false 2016 10 7 1 tr
  • 在 MySQL 数据库中保持 TEXT 字段唯一的最佳方法

    我想让 TEXT 字段的值在我的 MySQL 表中唯一 经过小型研究 我发现由于性能问题 每个人都不鼓励在 TEXT 字段上使用 UNIQUE INDEX 我现在想用的是 1 创建另一个字段来包含 TEXT 值的哈希值 md5 text v
  • 使用函数的 SQL 查询 - 如何获取列表的最大计数

    如何查询 MAXIMUM COUNT 交易次数 我的代码如下 SELECT customer id COUNT customer id FROM rental GROUP BY customer id HAVING MAX COUNT cu

随机推荐

  • PyInstaller编译后,如何不出现执行exe文件时弹出的命令行窗口

    如果你使用PyInstaller将Python脚本编译为可执行文件 exe 默认情况下会在运行时显示一个命令行窗口 如果你想要隐藏命令行窗口并使程序在后台静默运行 你可以采用以下方法之一 使用 noconsole选项编译 在使用PyInst
  • 路由选择协议(一) RIP协议

    在介绍路由协议 RIP OSPF BGP 之前会向大家介绍补充一些基本的概念 以便能够更容易的理解本文 废话不多说我们开始 一 自治系统 自治系统 Autonomous system 通俗的讲就是我们把全球互联网分成若干个区域 每个区域由一
  • 【论文精读】KD-MVS

    今天读的是发表在ECCV2022上的自监督MVS文章 作者来自于旷视科技和清华大学 文章链接 arxiv 代码链接 https github com megvii research KD MVS 目录 Abstract 1 Introduc
  • Ubuntu20.04.6+ros2 foxy配置rosdepc

    问题 在根据古月居ros2教程工作空间一节时 在安装自动依赖 rosdepc install i from path src rosdistro humble y 出现问题 ERROR the following packages stac
  • 一个超强 Android 进阶路线知识图谱

    早上好 我是 AndroidGuider 公众号 Android 指南 专注于推荐 GitHub 上 Android 有用有趣的项目 挖掘开源的价值 欢迎关注 今天推荐一个非常优质的项目 Android 最全进阶路线知识图谱 干货资料 作者
  • 正定矩阵

    转 https www jianshu com p e969a50bb0a5 utm campaign maleskine utm content note utm medium seo notes utm source recommend
  • Oracle SQL语句进行新建用户

    哈喽大家好 随着科技的发达 国家的强大 人类智慧的增长 我们的身边也越来越多所谓的人工智能 但是他们所谓的智能也是我们人类去进行编程代码 进行设定功能和各种精密的传感器来进行的 自然软件的开发也就成了不可缺少的重要部分 下面我们要讲的是or
  • 像梦一样

    近日 听说的小学同学的父亲去世了 好多年不见 印象中还是小学时的样子 没想到今年去世了 转念一想 跟我父亲差不多的年纪 还没有老啊 就已经到了有人离开的年纪啦 回头一想 好久没给我的父亲打电话了 上次好好打电话还是去年有宝宝的时候吧 最近半
  • 信息技术导论论文

    摘要 计算机系统对存储器的容量 速度和价格这三个基本性能指标都有一定的要求 存储容量应确保各种应用的需要 存储器速度应尽量与CPU的速度相匹配并支持I O操作 存储器的价格应比较合理 但是存储器的速度越快 则每位的价格就越高 存储器的容量越
  • kafka 一个topic 被多个group.id 自动创建

    多遍看关于kafka 的原理 group与partition 与topic 的关系 自我感觉理解没问题了 写了一个netty结合kafka 5秒发送一个心跳 拉取一次消息 得到相应的record 读取记录提交offset 我用的是 两个gr
  • Xadmin(踩坑)——cannot import name ‘pretty_name‘

    问题 ImportError cannot import name pretty name 解决问题 修改前 修改后
  • maven package Failed to collect dependencies at com.study:*****:jar:1.1-SNAPSHOT

    聚合父pom C集成A pom B pom 而A pom又依赖B pom 此时package打包A 报错Failed to collect dependencies at com study jar 1 0 SNAPSHOT 解决 先ins
  • Spring -- Spring boot Spring mvc 的区别

    spring boot与spring mvc的区别是什么 知乎如题https www zhihu com question 64671972 作者 潜龙勿用 链接 https www zhihu com question 64671972
  • kubectl get csr/node 提示:bash: kubectl: 未找到命令...

    报错信息 root master kubectl get csr 查看节点请求 bash kubectl 未找到命令 root master kubectl get node 查看集群节点 bash kubectl 未找到命令 解决方法 查
  • 关于Pictureselector相册全白、不显示图片的问题(附GlideEngine代码)

    关于Pictureselector相册全白 不显示图片的问题 文末附GlideEngine代码 前些日子要做一个用到相机相册剪裁照片的项目 用了网上比较火比较新的Pictureselector 发现相册能识别并且打开 但是整个相册预览都是空
  • Spring Boot整合dubbo+Nacos实战(二)

    文章目录 前言 新建父工程cloud alibaba demo 新建子工程dubbo demo service 新建子工程gtw 启动工程 测试 前言 nacos的实战在前面一章已经介绍到 Spring Cloud Alibaba Naco
  • BSC什么意思

    封装尺寸图的 BSC 先查Google 一说BSC是指基本值 Basic 其它还有TYP 典型值 REF参考值 单位一般会在资料里说明 二说是Basic Spacing between Centers 三说BSC不是单位 意思是没有误差的
  • 关于‘vue‘ 不是内部或外部命令,也不是可运行的程序或批处理文件的解决办法

    一般出现 vue 不是内部或外部命令 也不是可运行的程序或批处理文件的提示 一般有两种可能 一是当前环境无法找到Vue cli路径 二是没有配置npm环境变量 下面具体讲解解决办法 一 当前环境无法找到Vue cli路径 解决办法执行以下操
  • 【gdb】超详细介绍

    文章目录 搭建试验环境 QuickStart gdb基本命令 run或r 运行程序 list 查看源代码 b break打断点 info 查看断点 Print 打印变量 x打印地址 Step 进某一个具体函数的调试 Step i按汇编命令进
  • Mysql 调优记: INNER JOIN查询 Using temporary; Using filesort 问题优化

    近期笔者在生产环境中发现一条执行非常慢的sql 大概时间为5s左右 于是乎对改SQL场景进行EXPLAIN 分析 发现一个在执行过程中出现对 Using temporary Using filesort 即在执行过程中产生了临时表来存储结果