浅谈SQL优化

避免使用子查询

例:

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.mzph.cn/news/628869.shtml

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

相关文章

10.9.2 std::function 代替函数指针 Page182~183

std::function是一个模板类&#xff0c;基本可作为函数指针的代替品&#xff0c;具备更多功能&#xff0c;特别是与函数对象及bind配合使用。使用std::function时&#xff0c;需要添加头文件 #include <functional> 1.定义函数指针 18行&#xff0c;定义了一个函数指针类…

SpringBoot的yml多环境配置3种方法

文章目录 SpringBoot的yml多环境配置3种方法1、多个yml文件1.1、创建多个配置文件applicaiton.yml中指定配置 2、单个yml文件3、在pom.xml中指定环境配置3.1、创建多个配置文件3.2、在application.yml中添加多环境配置属性3.3、在pom.xml中指定使用的配置3.4、问题&#xff1a;…

Mysql root 密码重置详解

文章目录 1 概述1.1 前言1.2 mysql 版本查询 2 windows 操作系统2.1 mysql 8 及以上版本2.1.1 关闭 mysql 服务2.1.2 通过无认证方式启动 mysql2.1.3 新开窗口&#xff0c;登录 mysql&#xff0c;重置密码 1 概述 1.1 前言 不同的操作系统&#xff08;如&#xff1a;windows、…

Android PendingIntent 闪退

先来给大家推荐一个我日常会使用到的图片高清处理在线工具&#xff0c;主要是免费&#xff0c;直接白嫖 。 有时候我看到一张图片感觉很不错&#xff0c;但是图片清晰度不合我意&#xff0c;就想有没有什么工具可以处理让其更清晰&#xff0c; 网上随便搜下就能找到&#xff…

Spring Boot 3 + Vue 3实战:实现用户登录功能

文章目录 一、实战概述二、实战步骤​&#xff08;一&#xff09;创建前端项目 - login-vue1、创建Vue项目2、安装axios模块3、安装vue-router模块4、安装less和less-loader模块5、运行Vue项目6、在浏览器里访问首页7、在IDEA里打开Vue项目8、创建登录Vue组件9、创建首页Vue组件…

自动驾驶中的坐标系

自动驾驶中的坐标系 自动驾驶中的坐标系 0.引言1.相机传感器坐标系2.激光雷达坐标系3.车体坐标系4.世界坐标系4.1.地理坐标系4.2.投影坐标系4.2.1.投影方式4.2.2.墨卡托(Mercator)投影4.2.3.高斯-克吕格(Gauss-Kruger)投影4.2.4.通用横轴墨卡托UTM&#xff08;UniversalTransve…

MySQL视图索引基础练习

表定义 学生表&#xff1a;Student (Sno, Sname, Ssex , Sage, Sdept) 学号&#xff0c;姓名&#xff0c;性别&#xff0c;年龄&#xff0c;所在系 Sno为主键 课程表&#xff1a;Course (Cno, Cname,) 课程号&#xff0c;课程名 Cno为主键 学生选课表&#xff1a;SC (Sno, C…

Android 布局菜鸟 android中的布局类型和特点?

一、LinearLayout(线性布局) 1、 特点: 主要以水平或垂直方式来排列界面中的控件。并将控件排列到一条直线上。在线性布局中,如果水平排列,垂直方向上只能放一个控件,如果垂直排列,水平方向上也只能放一个控件。 2、适⽤场景: Android开发中最常见的 ⼀种布局⽅式,排列…

CUDA tips

命令行查看核函数消耗的寄存器和共享内存数量 nvcc --ptxas-options-v reduce_sum.cu nvprof 使用 由于 8.0 及以上计算能力的显卡用不了 nvprof&#xff0c;官方建议用 nsight system 和 ncu&#xff0c;但是如果只想命令行打印表格查看 kernel 概况感觉还是 nvprof 方便&am…

Android 系统启动过程纪要(基于Android 10)

前言 看过源码的都知道&#xff0c;Launcher系统启动都会经过这三个进程 init ->zygote -> system_server。今天我们就来讲解一下这三个进程以及Launcher系统启动。 init进程 准备Android虚拟机环境&#xff1a;创建和挂载系统文件目录&#xff1b;初始化属性服务&…

SAP银企直联报错排查方法与步骤-F110

银企直联的报错排查经常需要利用F110来查询。方法步骤如下&#xff1a; 1、首先要确定报错是哪天的&#xff0c;并且当天那一次跑的付款建议。需要通过表 REGUH来确认(跟据供应商编码、日期) 2、通过REGUH表的信息知道了是2024年1月16号第5个标识&#xff08;也就是第五次跑付…

Mac OS系统 SVN客户端 smartSVN 安装和基础使用

一、下载SVN客户端 官网地址&#xff0c;可以根据自己的系统下载 https://www.smartsvn.com/download/ 二、安装客户端和激活 第一步安装&#xff0c;很简单。 第二步&#xff0c;激活&#xff0c;选择激活文件 创建一个许可文件&#xff0c;例如 smartSvn.license。 内容如…

搜维尔科技:SenseGlove Nova 2力反馈技术手套,虚拟培训的沉浸感达到新高度!

SenseGlove Nova 2-虚拟培训的沉浸感达到新高度&#xff01; 通过集成主动接触反馈&#xff0c;Nova 2 使用户能够在手掌中感知虚拟现实物体的感觉。虚拟训练、研究和多人互动现在感觉比以往更加自然。这项创新增强了与整个手掌接触的任何虚拟物体的真实感。使用第一款也是唯一…

el-date-picker组件设置时间范围限制

需求&#xff1a; 如图所示&#xff0c;下图为新增的一个弹层页面&#xff0c;同时有个需求&#xff0c;日期选择需要限制一个月的时间范围&#xff08;一月默认为30天&#xff09;&#xff1a; 查看官方文档我们需要主要使用到如下表格的一些东西&#xff1a; 参数说明类型可…

Spring Cloud 微服务中 gateway 网关如何设置健康检测端点

主要是为了让 k8s 识别到网关项目已经就绪&#xff0c;但是又不想在里面通过 Controller 实现。因为在 Controller 中这样做并不是最佳实践&#xff0c;因为 Gateway 的设计初衷是专注于路由和过滤&#xff0c;而不是业务逻辑的处理。 在 Gateway 中配置健康检查端点可以通过以…

最佳实践分享:SQL性能调优

SQL性能调优是一个需要不断探索和实践的过程&#xff0c;旨在确保数据库查询的高效运行。本文将分享一些SQL性能调优的最佳实践&#xff0c;帮助您提升数据库性能&#xff0c;减少查询响应时间。 一、索引优化 索引是提高查询性能的关键。以下是一些关于索引优化的建议&#…

使用 Apache POI 更新/覆盖 特定的单元格

使用 Apache POI 更新特定的单元格 一. 需求二. 实现三. 效果 一. 需求 将以下表中第4行&#xff0c;第4列的单元格由“张宇”更新为“汤家凤”&#xff0c;并将更行后的结果写入新的Excel文件中&#xff1b; 二. 实现 使用Apache POI&#xff0c;可以精确定位到需要更改的单…

LeetCode、2336. 无限集中的最小数字(中等,小顶堆)

文章目录 前言LeetCode、2336. 无限集中的最小数字题目链接及类型思路代码题解 前言 博主所有博客文件目录索引&#xff1a;博客目录索引(持续更新) LeetCode、2336. 无限集中的最小数字 题目链接及类型 题目链接&#xff1a;2336. 无限集中的最小数字 类型&#xff1a;数据…

VC++中使用OpenCV对原图像中的四边形区域做透视变换

VC中使用OpenCV对原图像中的四边形区域做透视变换 最近闲着跟着油管博主murtazahassan&#xff0c;学习了一下LEARN OPENCV C in 4 HOURS | Including 3x Projects | Computer Vision&#xff0c;对应的Github源代码地址为&#xff1a;Learn-OpenCV-cpp-in-4-Hours 视频里面讲…

ChatGPT新出Team号 年付费

之前一直传的团队版ChatGPT终于来了&#xff0c;这个对拼单的比较合算。每人每月25美元&#xff0c;只能按年支付。 团队版比普通版多的权益有&#xff1a; ◈更多的GPT-4消息上限&#xff0c;三小时100次。 ◈可以创建与团队内部共享的GPTs。 ◈用于工作空间管理的管理员控…