浅谈SQL优化

news2024/11/19 10:46:07

避免使用子查询

例:

select * from t1 where id in (select id from t2 where name = 'lolly1023');

其子查询在MySQL5.5版本里,内部执行计划是:先查询外表再匹配内表,而不是先查内表t2,当外表的数据很大时,查询的速度会非常慢。

MariaDB10/MySQL5.6版本里,采用join关联方式对其进行了优化,这条SQL语句会自动转化为:

select t1.* from t1 left join t2 on t1.id = t2.id where t2.name = "lolly1023";

还有一些原因就是,执行子查询时,会创建临时表,查询完毕之后再删除,所以子查询的速度会收到影响。

join查询的话,小表驱动大表,只适合用较少数据量的适合,如果数据量较大的时候,还是推荐把业务逻辑放到应用层中做关联。

数据来源:探讨MySQL多表查询:使用JOIN还是相关子查询,谁才是合适之举?

用IN来替换OR

  • 低效查询
select * from t1 where id = 10086 or id = 580058 or id = 980634;
  • 高效查询
select * from t1 where id in (10086,580058,980634);

另外,MySQL对于IN做了响应的优化,即将IN中的常量全部存储在一个数组里面,而且这个数组是排好序的。但是如果数值较多,产生的消耗也是比较大的。再例如:

select * from t1 where id in (10086,10087,10088);

对于连续的数值,能用BETWEEN就不要用IN 了;再或者使用连接来替换。

在数据量较少的时候,INOR的效率基本一致,非索引字段IN的效率优于OR

  1. OR的效率为O(n)
  2. IN的效率为O(logn),当n越大的时候效率相差越明显。

数据来源:or 和 in 的效率对比

读取适当的记录LIMIT M,N,而不要读多余的记录

select * from t1 limit 99952,30;

使用上述SQL做分页的时候,可能会发现,随着表数据量的增加,直接使用limit分页查询会越来越慢。

对于limit m,n的分页查询,越往后面翻页(即m越大的情况下),SQL的耗时会越来越长,对于这种应该先取出主键id,然后通过主键id跟原表进行join关联查询。因为MySQL并不是跳过offset行,而是取offset+N行,然后放弃前offset行,返回N行,那当offset特别大的时候,效率就会非常的地下,要么控制返回的总页数,要么对超过特定阈值的页数进行SQL改写。

优化的方法如下:可以取前一夜的最大行数的id(将上次遍历到的最末尾的数据ID传给数据库,然后直接定位到该ID处,再往后遍历数据),然后根据这个最大的id来限制下一页的起点。比如此列中,上一页最大值的id99952sql可以采用如下的写法:

select * from t1 where id > 99952 limit 30;

或者使用索引覆盖来优化,可以先查出索引的ID,然后根据ID拿数据,可以采用以下写法:

select * from (select id from t1 limit 99952,30) a left join t1 b on a.id = b.id;

数据来源:MySQL limit使用及超大分页问题解决

禁止非必要的Order By排序

如果我们对结果没有排序的要求,就尽量少使用排序;

如果排序字段没有用到索引,也尽量少用排序;

另外,分组统计查询时可以禁止其默认排序

select s_id,count(*) from t2 group by s_id;

默认情况下,MySQL会对GROUP BY col1,col2的字段进行排序,也就是说上述会对s_id机械能排序,如果想要避免排序结构的消耗,可以指定ORDER BY NULL禁止排序:

select s_id,count(*) from t2 group by s_id order by null;

数据来源:浅析order by中如何处理null以及order by null的作用

总和查询可以禁止排重用union all

unionunion all的差异主要是前者需要将结果集合并后再进行唯一性的过滤操作,这就会涉及到排序,增加大量的CPU运算,加大资源消耗及延迟。

当然,union all的前提条件是两个结果集没有重复的数据。所以一般是我们明确知道不会出现重复数据的时候才建议使用union all提高速度。

避免随机取记录

select * from t1 where 1 = 1 order by rand() limit 4;
select * from t1 where id>= ceil(rand()*1000) limit 4;

以上两个语句都无法用到索引

将多次插入换成批量Insert插入

将多次插入:

insert into t(id,name) values(1,'a');
insert into t(id,name) values(2,'b');
insert into t(id,name) values(3,'c');

换成批量插入:

insert into t(id,name) values(1,'a'),(2,'b'),(3,'c');

主要还是因为事务的原因,多次插入的话会有多个事务,但是批量插入的话,只需要一次事务。

数据来源:MySQL批量插入为什么比单条插入快很多

只返回必要的列,用具体的字段列表代替select * 语句

select 语句会增加很多不必要的消耗(CPUIO,内存,网络带宽);增加了使用覆盖索引的可能性;当表结构发生改变时,前者也需要经常更新。所以要求直接在select*后面加上字段名。

MySQL数据库是按照行的方式存储,而数据库取操作都是以一个页大小进行IO操作的,每个IO单元中存储了多行,每行都是存储了该行的所有字段。所以无论取一个字段还是多个字段,实际上数据库在表中需要访问的数据量其实是一样的。

但是如果查询的字段都在索引中,也就是覆盖索引,那么可以直接从索引中获取对应的内容直接返回,不需要进行回表减少IO操作。除此之外,每当order by操作的时候,select字句中的字段多少会在很大程度上影响到我们排序的效率。

数据来源:到底为什么不建议使用SELECT * ?

区分IN和EXISTS

select * from t1 where id in (select id from t2);

上面的语句相当于:

select * from t1 where exists(select * from t2 where t1.id = t2.id)

区分inexists主要是造成了驱动顺序的改变(性能变化的关键),如果是exists,那么以外层表为驱动表先被访问,如果是in,那么先执行子查询。所以in适合于外表大而内表小的情况;exists适合于外表小而内表大的情况。

另外,in查询在某些情况下有可能会查询返回错误的结果,因此,通常是建议在确定且有限的集合时,可以使用in。如in (0,1,2)

数据来源:in 和 exists的区别

优化Group By语句

如果group by语句的结果没有排序要求,那就在语句后面加上 order by null,因为group by 默认会进行排序;

尽量让group by过程用上表的索引,确认方法是explain结果里没有Using temporaryUsing filesort;

  • 如果group by需要统计的数据量不大,尽量只使用内存临时表;也可以通过适当调大tmp_table_size参数,来避免使用到磁盘临时表;
  • 如果数据量实在是太大,使用sql_big_result这个提示,来告诉优化器直接使用排序算法(直接用磁盘临时表)得到group by的结果。

使用where替换having字句:避免使用having字句,having只会在检索出所有记录之后才会对结果集进行过滤,这个处理需要排序分组,如果能通过where字句提前进行过滤查询的目数,就可以减少这方面的开销。

  • 低效
select id,avg(id) from t2 group by id having id = 100086 or id = 5555;
  • 高效
select id,avg(id) from t2 where id = 100086 or id = 5555 group by id;

数据来源:MySQL数据库 group by 语句怎么优化?

尽量使用数字型字段

若只含数值信息的字段尽量不要设计为字符型,这样会降低查询和连接的性能。引擎在处理查询和连接时会逐个比较字符串中每一个字符,而对于数字型而言只需要比较一次就够了。

数据来源:mysql 选择数值还是字符效率高

优化Join语句

当我们执行两个表的Join的时候,就会有一个比较的过程,逐条比较两个表的语句是比较慢的,因此可以把两个表中数据以此读进一个内存块中,在MySQL中执行:

show variables like 'join_buffer_size';

这样可以看到Join在内存中的缓存池大小,其大小将会影响Join语句的性能。在执行Join的时候,数据库会选择一个表把它要返回以及需要进行和其他表进行比较的数据放入join_buffer

什么是驱动表,什么是被驱动表,这两个概念在查询中有时容易让人搞混,有下面几种情况,大家需要了解。

  1. 当连接查询没有where条件时:

left join 前面的表是驱动表,后面的表是被驱动表

right join 后面的表是驱动表,前面的表是被驱动表

inner join / join 会自动选择表数据比较少的作为驱动表

straight_join(≈join) 直接选择左边的表作为驱动表(语义上与join类似,但去除了join自动选择小表作为驱动表的特性)

  1. 当连接查询有where条件时,带where条件的表是驱动表,否则是被驱动表

假设有表如右边:t1t2表完全一样,a字段有索引,b无索引,t1有100条数据,t2有1000条数据

若被驱动表有索引,那么其执行算法为:Index Nested-Loop Join(NLJ),示例如下:

  1. 执行语句:select * from t1 straight_join t2 on (t1.a=t2.a);由于被驱动表t2.a是有索引的,其执行逻辑如下:
  • 从表t1中读入一行数据 R
  • 从数据行R中,取出a字段到表t2里去查找
  • 取出表t2中满足条件的行,跟R组成一行,作为结果集的一部分
  • 重复执行步骤1到3,直到表t1的末尾循环结束
  • 如果一条join语句的Extra字段什么都没写的话,就表示使用的是NLJ算法

img

若被驱动表无索引,那么其执行算法为:Block Nested-Loop Join(BLJ)Block 块,每次都会取一块数据到内存以减少I/O的开销),示例如下:

  1. 执行语句:select * from t1 straight_join t2 on (t1.a=t2.b);由于被驱动表t2.b是没有索引的,其执行逻辑如下:
  • 把驱动表t1的数据读入线程内存join_buffer(无序数组)中,由于我们这个语句中写的是select *,因此是把整个表t1放入了内存;
  • 顺序遍历表t2,把表t2中的每一行取出来,跟join_buffer中的数据做对比,满足join条件的,作为结果集的一部分返回。

img

  1. 另外还有一种算法为Simple Nested-Loop Join(SLJ),其逻辑为:顺序取出驱动表中的每一行数据,到被驱动表去做全表扫描匹配,匹配成功则作为结果集的一部分返回。

另外,Innodb会为每个数据表分配一个存储在磁盘的 表名*.ibd* 文件,若关联的表过多,将会导致查询的时候磁盘的磁头移动次数过多,从而影响性能

所以实践中,尽可能减少Join语句中的NestedLoop的循环次数:“永远用小结果集驱动大的结果集”

  • 用小结果集驱动大结果集,将筛选结果小的表(在决定哪个表做驱动表的时候,应该是两个表按照各自的条件过滤,过滤完成之后,计算参与join的各个字段的总数据量,数据量小的那个表,就是“小表”)首先连接,再去连接结果集比较大的表,尽量减少join语句中的Nested Loop的循环总次数
  • 优先优化Nested Loop的内层循环(也就是最外层的Join连接),因为内层循环是循环中执行次数最多的,每次循环提升很小的性能都能在整个循环中提升很大的性能;
  • 对被驱动表的join字段上建立索引;
  • 当被驱动表的join字段上无法建立索引的时候,设置足够的Join Buffer Size
  • 尽量用inner join(因为其会自动选择小表去驱动大表).避免 LEFT JOIN (一般我们使用Left Join的场景是大表驱动小表)和NULL,那么如何优化Left Join呢?
  • 条件中尽量能够过滤一些行将驱动表变得小一点,用小表去驱动大表
  • 右表的条件列一定要加上索引(主键、唯一索引、前缀索引等),最好能够使type达到ange及以上(ref,eq_ref,const,system
  • 适当地在表里面添加冗余信息来减少join的次数
  • 使用更快的固态硬盘

性能优化,left join 是由左边决定的,左边一定都有,所以右边是我们的关键点,建立索引要建在右边。当然如果索引是在左边的,我们可以考虑使用右连接,如下:

-- 最好在bid上建索引
select * from atable left join btable on atable.aid=btable.bid;

TipsJoin左连接在右边建立索引;组合索引则尽量将数据量大的放在左边,在左边建立索引。

数据来源:如何进行JOIN优化?

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VM安装群晖系统 挂载整个硬盘给群晖系统

前言 在我们日常业务需求中,经常需要把整个磁盘的空间分配给群晖使用,那么如何通过vm分配整个磁盘空间给群晖系统。 操作 需要用管理员运行VM虚拟机 然后添加硬盘 就可以有权限全部添加了。这样会清除要挂载的磁盘的全部的数据。

蓝莓产量预测(R语言版)

数据描述 字段名 描述 字段名 描述 id 蓝莓唯一标识 MinOfUpperTRange 花期内最高温带日平均气温的最低记录, Clonesize 蓝莓克隆平均大小 AverageOfUpperTRange 花期内最高温带日平均气温, Honeybee 蜜蜂密度 MaxOfLowerTRange 花期内最低温带日平均气温的最…

vscode显示120字符或者80字符提示线或者显示垂直标尺

vscode显示120字符或者80字符提示线或者显示垂直标尺 一般规定一行代码不超过80或者120个字符。取决于团队的编码规范。 不同公司不同团队有不同的规定。 当单行代码过长。产生横向滚动条。使得代码难以阅读。 打开全局设置的settings.json /C:/Users/xxx/AppData/Roaming/Cod…

Qt-UI界面无法输入名字

在UI界面“在这里输入”,直接双击填写名称,无论是中文还是英文都没有反应。解决方案: 双击“在这里输入之后”,在可编辑状态下,空格→enter键,然后在右下角属性框的title中直接填写中文或英文名&#xff0…

学习c语言,奇偶排序

如果左边是奇数右边是偶数就不管他,如果左边找到偶数右边是奇数则互相交换。

高性能小模型SLM最新优化方案和热门应用盘点,附配套模型和开源代码

当大多数人都还在卷谁的大模型参数规模大的时候,聪明人已经开始搞“小模型”了(doge)。 这里的小模型指的小型语言模型(Small Language Model,简称SLM),通常用于解决资源受限或实时性要求较高的…

「云渲染科普」3dmax vray动画渲染参数如何设置

动画渲染一直都是占用时间最多的地方,动画帧数通常 1 秒在 25 帧或者以上,电脑通常需要对每一帧的画面分批渲染,通常本地电脑由于配置上的限制,往往无法在短时间内快速的完成渲染任务。这时“云渲染”则成为了动画渲染的主要方案&…

在国外外出结账时应该怎样表述,柯桥生活英语学习

大家平时外出就餐时,尤其是那种外国餐厅,结账时都怎么表达?可能大家会脱口而出“how much”..... 如果你真的这么说,那可就“踩雷”了。因为,在歪果仁的文化中,用how引导的疑问句都或多或少涉及隐私问题&am…

「许战海矩阵战略洞察」吉香居给调味品企业带来的战略启示

引言:吉香居通过实施份额化战略和打造形象产品,在调味品行业中取得了成功。但品牌结构需要调整,需要将子品牌整合到吉香居主品牌下,共同提升品牌势能。此外,企业需保持主品牌竞争战略,以实现长期稳定的高速…

一、基础篇 vue模板语法

Vue.js 使用了基于 HTML 的模板语法,允许开发者声明式地将 DOM 绑定至底层 Vue 实例的数据。所有 Vue.js 的模板都是合法的 HTML,所以能被遵循规范的浏览器和 HTML 解析器解析。 在底层的实现上,Vue 将模板编译成虚拟 DOM 渲染函数。结合响应…

sqli-labs关卡25(基于get提交的过滤and和or的联合注入)

文章目录 前言一、回顾上一关知识点二、靶场第二十五关通关思路1、判断注入点2、爆字段个数3、爆显位位置4、爆数据库名5、爆数据库表名6、爆数据库列名7、爆数据库数据 总结 前言 此文章只用于学习和反思巩固sql注入知识,禁止用于做非法攻击。注意靶场是可以练习的…

OPC UA 开源库编译方法及通过OPC UA连接西门S7-1200 PLC通信并进行数据交换[一]

前言 在现代工业自动化领域,OPC UA(开放性生产控制和统一架构)是一种广泛应用的通信协议。本文将以通俗易懂的方式解释OPC UA的含义和作用,帮助读者更好地理解这一概念。 一、OPC UA的定义 OPC UA全称为“开放性生产控制和统一…

Springboot注解@Profile的作用

目录 作用 使用场景 使用技巧 激活配置文件 生产例子 作用 Springboot这个注解说白了就是用于区分有些组件、类、方法在不同环境下是否要注入启动的一个注解,打个比方我有个swagger配置类,只要注入这个类在spring中我就可以使用Swaggger这个插件&am…

x-cmd pkg | tokei - 代码行数统计工具

目录 简介首次用户技术特点竞品和相关作品进一步阅读 简介 tokei 是一个使用 Rust 编写的显示有关代码统计信息的命令行工具,可以分门别类的统计目录内的代码行数。 tokei 具有良好的跨平台性,可以在 Linux、macOS、Windows 等多种平台上安装运行。 首…

呼叫中心的排班管理

排班管理是呼叫中心现场管理的重要内容之一。安排合适的人员在合适的地点和时间做合适的事情,这不仅仅是衡量排班质量的关键,也是管理的核心。而OKCC也是基于多年对呼叫中心提供服务,理解排班的业务流程,提供充分的数据支持。 呼叫…

Kafka 简介

目录 1、概念介绍 Kafka 由来 ZooKeeper Kafka 特性 Kafka 使用场景 Kafka 复制备份 2、Kafka 架构 Broker Topic Producer Partition Consumers Consumer Group Distribution 1、概念介绍 Kafka 由来 Kafka 是最初由 Linkedin 公司开发,是一个分布…

kubesphere DevOps部署SpringCloud项目

🍎devops部署SpringCloud项目 🍅环境说明🍅部署流程🧁创建DevOps工程🧁填写流水线信息🧁创建流水线 🍅部署应用所需脚本🧁jenkinsfile🧁Dockerfile🧁deploy.y…

pyqtgraph绘图类

pyqtgraph绘图类 pyqtgraph绘图有四种方法: 方法描述pyqtgraph.plot()创建一个新的QWindow用来绘制数据PlotWidget.plot()在已存在的QWidget上绘制数据PlotItem.plot()在已存在的QWidget上绘制数据GraphicsLayout.addPlot()在网格布局中添加一个绘图 上面四个方法都接收同样…

Kurator v0.5.0发布,打造统一的多集群备份与存储体验

Kurator 是由华为云推出的开源分布式云原生套件。面向分布式云原生场景,Kurator 旨在为用户提供一站式的解决方案,帮助用户快速构建自己的分布式云原生平台。 在最新发布的 v0.5.0 版本中,Kurator 强化了其在多集群环境中的应用备份与恢复&am…