SQL语句优化

当表中有百万数据的时候,我们要怎么去查询数据,平时写的sql也许就会很慢了。

SQL的执行顺序

SELECT
DISTINCT <select_list>
FROM <left_table>
<join_type> JOIN <right_table>
ON <join_condition>
WHERE<where_condition>
GROUP BY <cgroup_by_list>
HAVING <having_condition>
ORDER BY <order_by_condition>
LIMIT <limit_number>

执行顺序:

  • FROM <表名># 选取表,将多个表数据通过笛卡尔积变成一个表。

  • ON <筛选条件> # 对笛卡尔积的虚表进行筛选

  • JOIN <join, left join, right join...>

  • <join 表> #指定join,用于添加数据到on之后的虚表中,例如leftjoin会将左表的剩余数据添加到虚表中

  • WHERE <where条件> #对上述虚表进行筛选

  • GROUP BY <分组条件> # 分组

  • <SUM() 等聚合函数> #用于having子句进行判断,在书写上这类聚合函数是写在having判断里面的

  • HAVING <分组筛选> # 对分组后的结果进行聚合筛选

  • SELECT <返回数据列表> #返回的单列必须在group by子句中,聚合函数除

  • DISTINCT 数据除重

  • ORDER BY 排序条件> # 排序

  • LIMIT <行数限制>

几个SQL优化技巧

这里总结了几种优化sql的小技巧

我准备了一百万的数据,表如下:

CREATE TABLE `test_import_export` (
  `seckill_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `order_id` varchar(51) NOT NULL COMMENT '订单id',
  `user_id` int(20) NOT NULL,
  `goods_id` int(11) NOT NULL,
  `msg` varchar(64) DEFAULT NULL,
  PRIMARY KEY (`seckill_id`),
  UNIQUE KEY `uidx_so_oid` (`order_id`) USING BTREE,
  UNIQUE KEY `uidx_so_id` (`seckill_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

1.写清楚需要查询的字段

select * from test_import_export;  -- 消耗2.006s

这个语句会查询所有字段,如果我们不需要字段比较长,比如msg这个字段,那这种写法就会增加查询时长。使用select *可能造成以下问题:

  • 性能问题

  • 维护问题

  • 查询结果不确定:当查询中存在多个表时,使用SELECT * 很容易造成列名冲突,导致查询结果不确定。

  • 冗余数据

select seckill_id,order_id,user_id,goods_id from test_import_export;  -- 0.712s

少查了msg字段后,可以看出快了不少。

2.避免索引失效的场景

2.1 使用in范围太大

in的范围太大会导致索引失效,not in 也会导致索引失效。

EXPLAIN select seckill_id,order_id,user_id,goods_id from test_import_export where order_id in (
    "share_of_sean_240001","share_of_sean_240002", ... -- 后面跟10000万条
)
EXPLAIN select seckill_id,order_id,user_id,goods_id from test_import_export where order_id not in (
    "share_of_sean_999982","share_of_sean_999983","share_of_sean_999984","share_of_sean_999985","share_of_sean_999986"
)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0jJIViOD-1693295196299)(file://d:\Pictures\mdpic\2023-08-28-16-46-14-image.png?msec=1693267693120)]

分析上面两个语句不会使用到索引,使用范围不大的in可以使用索引

EXPLAIN select seckill_id,order_id,user_id,goods_id from test_import_export where order_id in (
"share_of_sean_999982","share_of_sean_999983","share_of_sean_999984","share_of_sean_999985","share_of_sean_999986"
)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JcOiwc3I-1693295196300)(file://d:\Pictures\mdpic\2023-08-28-16-46-31-image.png?msec=1693267693120)]

如果使用in,需要限制in的数量。

但是使用>,<,BETWEEN ... AND ...这样的范围查找是可以使用索引的(无论范围多大),因为B+数的叶子节点已经排好序了,这样可以很容易的锁定范围。但是in里边的数据不确定,如果范围太大,优化器会分析,如果觉得全表扫描更快,就直接使用全表扫描。

解决办法将大量的in条件进行分页。然后再将结果组合起来。

2.2 前缀模糊查询

EXPLAIN 
select seckill_id,order_id,user_id,goods_id from test_import_export where order_id like '%_11';

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WyXuWy8n-1693295196300)(file://d:\Pictures\mdpic\2023-08-28-20-41-43-image.png?msec=1693267693120)]

前缀模糊查询会导致索引失效,导致全表扫描

EXPLAIN 
select seckill_id,order_id,user_id,goods_id from test_import_export where order_id like '11_%';

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nA7FnqFL-1693295196300)(file://d:\Pictures\mdpic\2023-08-28-20-39-52-image.png?msec=1693267693120)]

非前缀模糊查询则会使用到索引

2.3 数据区分度不大

select seckill_id,order_id,user_id,goods_id from test_import_export where `status` = 1 

如果在status字段上加索引,没有效果,因为索引是B+树,本来就是通过索引来进行排序增加检索速度,只有几个枚举值,自然也不需要索引就能很快检索。

2.4 在属性上计算(或使用函数,或隐式类型转换)不能使用索引

以下我就不贴出运行结果,直接解释原因。

EXPLAIN
select seckill_id,order_id,user_id,goods_id from test_import_export where seckill_id + 1 = 10;

因为索引保存的是索引字段的原始值,而不是 id+1(包括其他函数和表达式计算)计算之后的值,所以无法走索引。

这个解释似乎有些牵强,那为什么MySql不转换成 id = 10-1,我查过这个问题,觉得最好的解释是因为MySql不太想去实现这个逻辑,因为表达式和函数太多了,如果需要全部实现,显得代码太过臃肿。就需要程序员自己去注意这个问题。

对索引隐式类型转换

EXPLAIN
select seckill_id,order_id,user_id,goods_id from test_import_export where seckill_id = "71222593";

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Il1539OZ-1693295196300)(file://d:\Pictures\mdpic\2023-08-28-22-06-33-image.png?msec=1693267693121)]

数字 = 字符串,这种比较方式会使用索引。

EXPLAIN
select seckill_id,order_id,user_id,goods_id from test_import_export where order_id = 17;

字符串 = 数字,这种不会使用到索引。

原因是MySQL 在遇到字符串和数字比较的时候,会自动把字符串转为数字,然后再进行比较

相当于对order_i使用了函数:

EXPLAIN
select seckill_id,order_id,user_id,goods_id from test_import_export where CAST(order_id AS signed int)  = 17;

而第一种方式,相当于在右测使用了函数,自然能用到索引。

2.5 联合索引非最左匹配

联合索引在B+树中的存储方式如下图

在这里插入图片描述

其中c1是主键,联合索引c2,c3,c4。它首先根据联合索引第一列的值排序,如果第一列的值相等再根据第二列的值排序,以此类推。

联合索引需要遵循最左匹配原则,也就是按照最左优先的方式进行匹配。

如果创建了(a, b, c)三个联合索引

第一种情况:以下查询可以用到索引:

  • where a=1;

  • where a=1 and b=2 and c=3;

  • where a=1 and b=2;

其中a,b,c的顺序不重要,MySql会进行优化。

第二种情况:但是以下查询,则不会走索引:

  • where b=2;
  • where c=3;
  • where b=2 and c=3;

第三种情况:比较特殊:

  • where a = 1 and c = 3

    这种情况属于索引截断a能使用到索引,c不能。

    在5.5版本,前面a会走索引,在联合索引找到主键值后,回表查询出数据,然后对比c字段的值。

    5.6版本后,有一个索引下推功能,可以在索引遍历过程中,对索引中包含的字段先做判断,直接过滤掉不满足条件的记录,减少回表次数。

    什么是索引下推:五分钟搞懂MySQL索引下推

    大概原理是:截断的字段会被下推到存储引擎层进行条件判断(因为 c 字段的值是在 (a, b, c) 联合索引里的),然后过滤出符合条件的数据后再返回给 Server 层。由于在引擎层就过滤掉大量的数据,无需再回表读取数据来进行判断,减少回表次数,从而提升了性能。

第一,第二种情况体现了最左匹配原则,因为联合索引是按a,b,c依此排序,在a相同的情况下,对b排序,在b相同的情况下,对c排序。如果直接找b,根据树的特点就不能依据顺序快速定位b的位置。

2.6 使用OR

or两边,只要有一个不是索引,就会导致索引失效,原因很简单,如果有一边不是索引,那另一天即使有索引还是需要去匹配没有索引的一边是否符合条件,任然需要全表扫描。

解决办法是可以使用in代替or。或者分别查询在应用层面拼接。

3.慎用UNION关键字

在使用UNION执行完SQL后,会帮我们获取所有数据并去掉重复的数据,性能的损耗就在这里,而UNION ALL和UNION相反,帮我们获取所有数据但会保留重复的数据。

-- 2.5s
EXPLAIN
select seckill_id, order_id, user_id, goods_id from test_import_export where status = '0'
UNION
select seckill_id, order_id, user_id, goods_id from test_import_export where status = '1';

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yg2w9TkO-1693295196301)(file://d:\Pictures\mdpic\2023-08-29-14-39-09-image.png?msec=1693291149492)]

UNION会产生一张临时表,大致是用来去重的

-- 1.2s
EXPLAIN
select seckill_id, order_id, user_id, goods_id from test_import_export where status = '0'
UNION ALL
select seckill_id, order_id, user_id, goods_id from test_import_export where status = '1';

更换成union all执行时间上能快很多。

解决方法可以把两个语句拆开分别查(这里可以用多线程),然后将查询结果组合到一起。避免用unionunion all

4.小表驱动大表

加入表user_info,为小表(数据量不大),如下:

CREATE TABLE `user_info` (
  `user_id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) DEFAULT NULL,
  `sex` tinyint(2) DEFAULT NULL,
  PRIMARY KEY (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

有几种小表驱动大表的形式:

select user_id from test_import_export where user_id in 
(select user_id from user_info where user_id = 20)

5.分页查询优化

分页查询优化的方式主要有两种:

  • 最大id查询法。将上一次查询出的最大id传入,取每页数目的数据量。查询速度在ms级别

    select * from test_import_export where seckill_id > 44212577 ORDER BY seckill_id ASC
    limit 20;
    
  • 子查询方式。先分页查询出符合条件和分页的第一条id,然后取id>=的数据

    
    -- 子查询方式,1千万秒级别
    
    select * from test_import_export 
    where seckill_id >= ( select seckill_id from test_import_export limit 990000,1) limit 10;
    

以上两种方式都只适合不排序的情况。如果需要排序,则需要给排序加上索引,用索引字段来代替id字段比较:

select * from test_import_export 
where user_id >= ( select user_id from test_import_export limit 990000,1) limit 10;

6.优化select count

优化select count,只能从应用层面优化。

  • 加缓存(redis)

参考博客:

面试官:聊聊索引失效?失效的原因是什么? - 小林coding - 博客园

五分钟搞懂MySQL索引下推

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

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

相关文章

【Luniux】解决Ubuntu外接显示器不显示的问题

Luniux】解决Ubuntu外接显示器不显示的问题 文章目录 Luniux】解决Ubuntu外接显示器不显示的问题1. 检查nvidia显卡驱动是否正常2. 更新驱动3. 检查显示器是否能检测到Reference 1. 检查nvidia显卡驱动是否正常 使用命令行 nvidia-smi来检查显卡驱动是否正常&#xff0c;如果…

gRPC + Spring Boot 编程教程 - piot

在本文中&#xff0c;您将学习如何实现通过 gRPC 通信的 Spring Boot 应用程序。gRPC 是一个现代开源远程过程调用 (RPC) 框架&#xff0c;可以在任何环境中运行。默认情况下&#xff0c;它使用 Google 的 Protocol Buffer 来序列化和反序列化结构化数据。当然&#xff0c;我们…

Ceph入门到精通-LVS基础知识

LB集群: &#xff08;Load Balancing&#xff09;即负载均衡集群,其目的是为了提高访问的并发量及提升服务器的性能&#xff0c;其 实现方式分为硬件方式和软件方式。 硬件实现方式&#xff1a; 常用的有 F5公司的BIG-IP系列、A10公司的AX系列、Citrix公司的 NetScaler系列…

HUT23级训练赛

目录 A - tmn学长的字符串1 B - 帮帮神君先生 C - z学长的猫 D - 这题用来防ak E - 这题考察FFT卷积 F - 这题考察二进制 G - 这题考察高精度 H - 这题考察签到 I - 爱派克斯&#xff0c;启动! J - tmn学长的字符串2 K - 秋奕来买瓜 A - tmn学长的字符串1 思路&#x…

华为OD机试 - VLAN资源池 - 回溯、双指针(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路1、核心思想2、具体解题思路 五、Java算法源码六、效果展示1、输入2、输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&…

什么是NetDevOps

NetDevOps 是一种新兴的方法&#xff0c;它结合了 NetOps 和 DevOps 的流程&#xff0c;即将网络自动化集成到开发过程中。NetDevOps 的目标是将虚拟化、自动化和 API 集成到网络基础架构中&#xff0c;并实现开发和运营团队之间的无缝协作。 开发运营&#xff08;DevOps&…

敏捷研发管理软件及敏捷管理流程

Scrum中非常强调公开、透明、直接有效的沟通&#xff0c;这也是“可视化的管理工具”在敏捷开发中如此重要的原因之一。通过“可视化的管理工具”让所有人直观的看到需求&#xff0c;故事&#xff0c;任务之间的流转状态&#xff0c;可以使团队成员更加快速适应敏捷开发流程。 …

JVM知识点(二)

1、G1垃圾收集器 -XX:MaxGCPauseMillis10&#xff0c;G1的参数&#xff0c;表示在任意1s时间内&#xff0c;停顿时间不能超过10ms&#xff1b;G1将堆切分成很多小堆区&#xff08;Region&#xff09;&#xff0c;每一个Region可以是Eden、Survivor或Old区&#xff1b;这些区在…

【MySQL系列】MySQL复合查询的学习 _ 多表查询 | 自连接 | 子查询 | 合并查询

「前言」文章内容大致是对MySQL复合查询的学习。 「归属专栏」MySQL 「主页链接」个人主页 「笔者」枫叶先生(fy) 目录 一、基本查询回顾二、多表查询三、自连接四、子查询4.1 单行子查询4.2 多行子查询4.3 多列子查询4.4 在from子句中使用子查询 五、合并查询 一、基本查询回顾…

解密算法与数据结构面试:程序员如何应对挑战

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

Windows版本Docker安装详细步骤

文章目录 下载地址安装异常处理docker desktop requires a newer wsl 下载地址 https://desktop.docker.com/win/stable/Docker%20Desktop%20Installer.exe 安装 双击下载的文件Docker Desktop Installer.exe进行安装 点击OK 开始安装 安装完成点击Close and restart&…

电脑不安装软件,怎么将手机文件传输到电脑?

很多人都知道&#xff0c;AirDroid有网页版&#xff08;web.airdroid.com&#xff09;。 想要文件传输&#xff0c;却不想在电脑安装软件时&#xff0c;AirDroid的网页版其实也可以传输文件。 然而&#xff0c;要将文件从手机传输文件到网页端所在的电脑时&#xff0c;如果按…

“惠医通-医院挂号订单平台”

结合已学习过的vue3和TS完成的项目&#xff0c;便于患者对自己想要就诊的科室进行挂号&#xff0c;付款 一&#xff1a;项目简介 前端技术栈 Vue3 TS vue-router Element-ui Axios Pinia 项目架构 二&#xff1a;主要模块 1. axios二次封装 1.1 创建实例 //利用axios.creat…

视频融合平台EasyCVR视频汇聚平台关于小区高空坠物安全实施应用方案设计

近年来&#xff0c;随着我国城市化建设的推进&#xff0c;高楼大厦越来越多&#xff0c;高空坠物导致的伤害也屡见不鲜&#xff0c;严重的影响到人们的生命安全。像在日常生活中一些不起眼的小东西如烟头、鸡蛋、果核、易拉罐&#xff0c;看似伤害不大&#xff0c;但只要降落的…

Go【gin和gorm框架】实现紧急事件登记的接口

简单来说&#xff0c;就是接受前端微信小程序发来的数据保存到数据库&#xff0c;这是我写的第二个接口&#xff0c;相比前一个要稍微简单一些&#xff0c;而且因为前端页面也是我写的&#xff0c;参数类型自然是无缝对接_ 前端页面大概长这个样子 先用apifox模拟发送请求测试…

①matlab的命令掌握

目录 输入命令 命名变量 保存和加载变量 使用内置的函数和常量 输入命令 1.您可以通过在命令行窗口中 MATLAB 提示符 (>>) 后输入命令 任务 使用命令 3*5 将数值 3 和 5 相乘。 答案 3*5 2.除非另有指定&#xff0c;否则 MATLAB 会将计算结果存储在一个名为 ans…

美团面试拷打:ConcurrentHashMap 为何不能插入 null?HashMap 为何可以?

周末的时候,有一位小伙伴提了一些关于 ConcurrentHashMap 的问题,都是他最近面试遇到的。原提问如下(星球原贴地址:https://t.zsxq.com/11jcuezQs ): 整个提问看着非常复杂,其实归纳来说就是两个问题: ConcurrentHashMap 为什么 key 和 value 不能为 null?ConcurrentH…

MongoDB Long 类型 shell 查询

场景 1、某数据ID为Long类型&#xff0c;JAVA 定义实体类 Id Long id 2、查询数据库&#xff0c;此数据存在 3、使用 shell 查询&#xff0c;查不到数据 4、JAVA代码查询Query.query 不受任何影响 分析 尝试解决&#xff08;一&#xff09; long 在 mongo中为 int64 类型…

clickhouse(十四、分布式DDL阻塞及同步阻塞问题)

文章目录 一、分布式ddl 阻塞、超时现象验证方法解决方案 二、副本同步阻塞现象验证解决方案 一、分布式ddl 阻塞、超时 现象 在clickhouse 集群的操作中&#xff0c;如果同时执行一些重量级变更语句&#xff0c;往往会引起阻塞。 一般是由于节点堆积过多耗时的ddl。然后抛出…

论文阅读:Model-Agnostic Meta-Learning for Fast Adaptation of Deep Networks

前言 要弄清MAML怎么做&#xff0c;为什么这么做&#xff0c;就要看懂这两张图。先说MAML**在做什么&#xff1f;**它是打着Mate-Learing的旗号干的是few-shot multi-task Learning的事情。具体而言就是想训练一个模型能够使用很少的新样本&#xff0c;快速适应新的任务。 定…