热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Mysql竟然还有这么多不为人知的查询优化技巧,还不看看?

前言Mysql我随手造200W条数据,给你们讲讲分页
前言


Mysql 我随手造200W条数据,给你们讲讲分页优化 

MySql 索引失效、回表解析

今天再聊聊一些我想分享的查询优化相关点。


正文

准备模拟数据。

首先是一张 test_orde 表:

CREATE TABLE `test_order` (`id` INT(11) NOT NULL AUTO_INCREMENT,`p_sn` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`t_sn` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_general_ci',`type` TINYINT(4) NULL DEFAULT NULL,`create_time` DATETIME NULL DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
)

然后是一个存储过程 :

BEGIN DECLARE num INT DEFAULT 2000000; DECLARE i INT DEFAULT 0; WHILE i END

 执行存储过程,看下模拟数据:


开始。 


① 使用 count 、 group by  注意点

比如, 我们想统计一下 当前 表里面, 根据type维度 分别有多少 数据 :
 

SELECT COUNT(*) ,type
FROM test_order GROUP BY TYPE ;

目前可以看到我们现在数据库表 里面,其实type 就 1个 , 就是 1 。

真实场景,我们 肯定不止一个type。 

改造出模拟数据(尽量使数据更随机,真实业务场景也许会更加更加散乱):
 

将数据里面 id 是 7的 倍数的数据  的type 改成  5;

将数据里面 id 是 5 的 倍数的数据  的type 改成  2;

将数据里面 id 是 3 的 倍数的数据  的type 改成  4;

将数据里面 id 是 2 的 倍数的数据  的type 改成  3;

sql: 

UPDATE test_order a
INNER JOIN test_order b ON b.id % 7=0 AND a.id=b.id SET a.TYPE =5


UPDATE test_order a
INNER JOIN test_order b ON b.id % 5=0 AND a.id=b.id SET a.TYPE =2


UPDATE test_order a
INNER JOIN test_order b ON b.id % 3=0 AND a.id=b.id SET a.TYPE =4


UPDATE test_order a
INNER JOIN test_order b ON b.id % 2=0 AND a.id=b.id SET a.TYPE =3

看看效果 :

统计出 表里面 不同 type 类型 的 数据分别有多少条 ,且看看时间用了多久:

 

 看看 EXPLAIN :

 Using filesort : 通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所以并不是通过索引直接返回排序结果的排序都叫 FileSort 排序

可以看到,分析里面 出现了一个 using filesort , 这个玩意就是慢的原因。

可以看到 用到了 group by type , 返回来的数据 TYPE 是 1,2,3,4,5 默认 升序排好的。

是的,相当于 mysql 默认帮我们执行了排序, 无疑 这是需要花时间的。

所以说,当我们仅仅要的是 不同 type 数据的 统计数量结果, 那么我们是可以优化掉这个排序的耗时的。 

优化技巧 :


order by null 

我们在 group by 后面 加上 ORDER BY  NULL , 强制禁止排序  ,

看看效果 :

那有没有更加快的优化? 

有的, 加索引。 group by 是能命中索引的。

 加完索引效果:





②使用 left join  / right join 的注意点

关联查询, 比如 有 A 、 B  两个表 。

A表即是 我们的 test_order 表 200W条数据:

而B 表 是 test_order_detail 表  5W 条数据:

这两个表通过id、order_id 关联(简单举个例子)。

注意点:

1.当使用left join时,左表是驱动表,右表是被驱动表
2.当使用right join时,右表是驱动表,左表是被驱动表
3.当使用inner join时,mysql会默认自动选择数据量比较小的表作为驱动表,大表作为被驱动表

我们尽量要保证 小表 驱动 大表, 大小指的是数据量。

那么我们看 left join 来看看效果, A表 test_order 目前是大表  B表  test_order_detail是小表 效果:

我们使用  left join  , 故意把 大数据表放在 左, 小数据表放在右, 这时候 左大驱右小 ,

发现用了13秒,返回的是 200万条数据 
 

看看EXPLAIN分析情况: 

ps :
当查询引擎完成对行的计数时,结果集的其余部分出现。所以Heidi所谓的“网络时间”是计算行数的时间。这对于MyISAM来说实际上是瞬间的,而InnoDB需要一段时间。(heidiSQL编辑器)

那么如果我们反过来, 左小驱右大 :
 

 发现用了0.29秒,返回的是 5万条数据 

 看看EXPLAIN分析情况: 

可以看到 小表驱动大表的情况,时间效果的差距所在。

所以根据业务情况,必须要清晰地使用上 这个优化技巧 ,尽可能保证小表驱动大表。

为什么 ?

其实这个道理很简单, 驱动表 和 被 驱动表 , 就相当于 2层 for 循环遍历。

比如 大表200万数据 驱动 小表 5万数据 ,就是 :

for(int 驱动表行数&#61;0 ; 驱动表行数 <20000000; 驱动表行数&#43;&#43;){for (int 被驱动表行数&#61;0 ; 被驱动表行数<50000; 被驱动表行数&#43;&#43;){找出 驱动表行记录 条件 等于 被驱动表行记录 条件值}}

那可能很多初学者还是不明白&#xff0c; 放外面是 200W 循环&#xff0c;里面再嵌套 5W  是 200 乘以 5 &#xff1f;

那跟反过来5 乘以 200 有什么区别&#xff1f;  

简析&#xff1a;

可以看到上述的  EXPLAIN 大表驱动小表 或是 小表驱动大表&#xff0c; 可以看到 驱动表的索引都是不生效的&#xff0c; 生效的是 被驱动表的索引 。 

索引是b&#43;树&#xff0c;在索引上等值查询的时间复杂度为logN。


因为驱动表不走索引&#xff0c;需要全表扫描&#xff0c;而被驱动表可以建立索引加速查找。


若小表驱动大表&#xff0c;则时间复杂度为 5W*log200W
若大表驱动小表&#xff0c;则时间复杂度为 200W*log5W
 

所以 为什么 时间耗时久 &#xff0c;也就显然得知了。

是因为被驱动表又能命中索引&#xff0c;而且时间查找又快啊。




 ③ 对字段进行表达式操作 的注意点

比如 我们 想查出来 type  是  2 的 2倍 的数据 &#xff08;这里简单用type举例&#xff0c; 可能业务上更多是 传入一个参数,然后触发某某计算倍数的概念&#xff09;&#xff1a;

当我们 把 字段 type 融入到 表达式 里面时&#xff0c;可以看到 耗时 是 2.45&#43;秒 &#xff08;因为索引失效了&#xff09;&#xff1a;

  看看EXPLAIN分析情况&#xff1a; 

而我们把 type 字段 抽出来&#xff0c;不参与 表达式操作&#xff0c;我们发现效果一样&#xff0c;但是耗时只有 1.3 秒&#xff08;因为能命中索引&#xff09; &#xff1a;

  看看EXPLAIN分析情况&#xff1a; 


 ④ 对明确知道的条件值 使用 or 查询  还是  UNION ALL ,有说法

比如我们想查出表里面 type 是1  或者 type 是 5的 数据 , 如果我们使用 or 去实现 &#xff0c;大家知道的&#xff0c;使用or 是命中不了索引的&#xff0c;会全表扫描 。
 


很多这种时候&#xff0c;大家可能就会想&#xff0c; 遇到or 慢查询&#xff0c; 就换成  UNION ALL 呗 。
 

其实并不然 。

你可以理解为&#xff0c;当你使用or 查询 发现慢的时候&#xff0c; 你可以尝试使用UNION ALL 去替代调试 &#xff0c; 注意&#xff0c;是调试&#xff0c; 如果性能确实优化了&#xff0c;你就可以替代。

直接眼见为实 &#xff1a;

首先可以看到 union all  比 or 还要慢 。

甚至 还可以看看 in 的效果 &#xff0c;也是跟 or 基本一致 也是 3秒 左右 。

我们看看 使用 in的 EXPLAIN &#xff1a;

 再看看 使用 or  的 EXPLAIN  &#xff1a;

or 和 in  几乎是一样的 在不中索引的时候。

那看看   union all 的  EXPLAIN &#xff1a;
 

可以看到命中了 索引的。

但是为什么这时候   union all 反而慢呢&#xff1f; 

原因 &#xff1a;

1. 其实我们可以关注到 rows 和 filtered  

2. 数据量情况 以及散乱程度 

当全表扫描 98% 的数据 都是需要的&#xff0c; 一次扫描拿出结果。

而 union all 进行了 2次 扫描&#xff0c;虽然扫的是索引&#xff0c;但是扫了96万 &#43; 99 万 数据&#xff0c; 我们一共才200W数据。

 2次加起来 跟我们 全部扫描看到的row 199万 基本没区别。 

这时候就是看 数据的分布情况了。

继续看看 查询 三个 type &#xff1a;

使用 OR  &#xff1a;

 使用  union all  &#xff1a;

 再再再顺便再贴一个 示例 &#xff08;查询不同字段条件值的场景&#xff09;&#xff0c;让大家知道 or 和  union all  就是需要看实际情况调试使用的 &#xff1a;

所以 什么时候用 or  什么时候 用  union all  &#xff0c; 非绝对&#xff0c; 要调试为准&#xff08;特别是当你的union all 条件的字段也没索引的时候 &#xff0c;你想想扫描多次表的效率&#xff09;&#xff01; 




⑤ order by  的效能 提升 

先改造一下表  &#xff1a;


平时我们写代码&#xff0c;很多时候&#xff0c;我们一些复杂的业务sql拆分&#xff0c;我们很愿意去拆&#xff0c;提高效率。

但是遇到排序&#xff0c; 我个人就很懒&#xff0c;基本 就是 丢到sql上面 order by 了。

那么 这就有说法了。

模拟点数据 :

UPDATE test_order a
INNER JOIN test_order b ON b.id % 7&#61;0 AND a.id&#61;b.id SET a.i_amount &#61;99;
UPDATE test_order a
INNER JOIN test_order b ON b.id % 5&#61;0 AND a.id&#61;b.id SET a.i_amount &#61;66;
UPDATE test_order a
INNER JOIN test_order b ON b.id % 3&#61;0 AND a.id&#61;b.id SET a.i_amount &#61;588;
UPDATE test_order a
INNER JOIN test_order b ON b.id % 2&#61;0 AND a.id&#61;b.id SET a.i_amount &#61;88;

 可以看到现在 数据 有那么一些些乱了&#xff0c;可以来讲讲 order by 排序了 &#xff1a;

这时&#xff0c;如果我们 进行 组合 排序&#xff0c; 按照 i_amount  排序 然后再按照 type 排序&#xff0c; 我们会发现 &#xff0c;引擎有脾气&#xff0c;没有中索引&#xff0c;但是 在 extra上面 有说 用了 using filesort   


 

时间肯定是没有 直接用上 index 快的 &#xff1a;

 所以我们给它整活&#xff0c; 我们升级成组合索引 &#xff1a;

 

 这时候我们再执行&#xff0c;发现 可以命中了index 了&#xff1a;

好了&#xff0c;就先讲到这吧&#xff0c; 有空再讲其他。





推荐阅读
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • MySQL外键1对多问题的解决方法及实例
    本文介绍了解决MySQL外键1对多问题的方法,通过准备数据、创建表和设置外键关联等步骤,实现了用户分组和插入数据的功能。详细介绍了数据准备的过程和外键关联的设置,以及插入数据的示例。 ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • 本文介绍了如何在MySQL中将零值替换为先前的非零值的方法,包括使用内联查询和更新查询。同时还提供了选择正确值的方法。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 本文介绍了游标的使用方法,并以一个水果供应商数据库为例进行了说明。首先创建了一个名为fruits的表,包含了水果的id、供应商id、名称和价格等字段。然后使用游标查询了水果的名称和价格,并将结果输出。最后对游标进行了关闭操作。通过本文可以了解到游标在数据库操作中的应用。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 如何在php中将mysql查询结果赋值给变量
    本文介绍了在php中将mysql查询结果赋值给变量的方法,包括从mysql表中查询count(学号)并赋值给一个变量,以及如何将sql中查询单条结果赋值给php页面的一个变量。同时还讨论了php调用mysql查询结果到变量的方法,并提供了示例代码。 ... [详细]
  • 本文讨论了如何使用IF函数从基于有限输入列表的有限输出列表中获取输出,并提出了是否有更快/更有效的执行代码的方法。作者希望了解是否有办法缩短代码,并从自我开发的角度来看是否有更好的方法。提供的代码可以按原样工作,但作者想知道是否有更好的方法来执行这样的任务。 ... [详细]
  • 这篇文章主要介绍了Python拼接字符串的七种方式,包括使用%、format()、join()、f-string等方法。每种方法都有其特点和限制,通过本文的介绍可以帮助读者更好地理解和运用字符串拼接的技巧。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
  • 本文主要介绍了MySQL中子查询的基本用法和三种用法,包括生成参考值、内层查询与外层查询的比较操作以及使用事件号在成绩表中找到学生的分数记录。通过详细解析子查询的实例,帮助读者更好地理解和应用子查询。 ... [详细]
author-avatar
悠闲自在的鱼叫_688
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有