Java性能优化-书写高质量SQL的建议(如何做Mysql优化)

场景

Mysql中varchar类型数字排序不对踩坑记录:

Mysql中varchar类型数字排序不对踩坑记录_mysql vachar排序有问题-CSDN博客

为避免开发过程中针对mysql语句的写法再次踩坑,总结开发过程中常用书写高质量sql的一些建议。

注:

博客:
霸道流氓气质-CSDN博客

实现

查询SQL尽量不要使⽤select *而是select具体字段

理由:

只取需要的字段,节省资源、减少⽹络开销。

select * 进⾏查询时,很可能就不会使⽤到覆盖索引了,就会造成回表查询。

如果知道查询结果只有⼀条或者只要最大最小⼀条记录建议用limit 1

反例:

select id,name from student where name ='霸道'

正例:

select id,name from student where name ='霸道' limit 1

理由:

加上limit 1后,只要找到了对应的⼀条记录,就不会继续向下扫描了,效率将会⼤⼤提⾼。

当然,如果name是唯⼀索引的话,是不必要加上limit 1了,

因为limit的存在主要就是为了防⽌全表扫描,从⽽提⾼性能,

如果⼀个语句本身可以预知不⽤全表扫描,有没有limit ,性能的差别并不⼤。

应尽量避免在where子句中使用or来连接条件

反例:

select id,name from student where userid=1 or age = 18

正例:

使用union all

select id,name from student where userid=1 union all  select id,name from student where age = 18

理由:

使用or可能会使索引失效,从而全表扫描。

假设它走了userId的索引,但是走到age查询条件时,它还得全表扫描,也就是需要三步:全表扫描+索引扫描+合并,如果它一开始

就走全表扫描,直接一遍扫描就完事。

优化limit分页

反例:

select id,name from student limit 10000,10

正例:

方案1:返回上次查询的最大记录(偏移量)

select id,name from student where id > 10000 limit 10

方案2:order by + 索引

select id,name from student order by id limit 10000,10

方案3:在业务允许的情况下限制页数

select id,name from student order by id limit 10,10

理由:

当偏移量最⼤的时候,查询效率就会越低,因为Mysql并⾮是跳过偏移量直接去取后⾯的数据,

⽽是先把偏移量+要取的条数,然后再把前⾯偏移量这⼀段的数据抛弃掉再返回的。

如果使⽤优化⽅案⼀,返回上次最⼤查询记录(偏移量),这样可以跳过偏移量,效率提升不少。

⽅案⼆使⽤order by+索引,也是可以提⾼查询效率的。

优化like语句

⽇常开发中,如果⽤到模糊关键字查询,很容易想到like,但是like很可能让你的索引失效。

反例:

select id,name from student where name like '%badao'

正例:

select id,name from student where name like 'badao%'

理由:

Mysql中LIKE查询以%开头不一定会让索引失效。如果查询的结果中只包含主键和索引字段则会使用索引,反之则不会。

下面进行验证,在sname字段行添加普通索引

CREATE INDEX name_index on test_student(sname)

测试以%开头且只包含主键和索引字段,测试结果为使用索引

explain SELECT sid,sname,sage from test_student WHERE sname like '%三';

测试以%开头单包含主键和索引字段以及其它字段,测试结果为全表扫描

explain SELECT sid,sname,sage from test_student WHERE sname like '%三';

测试以%结尾且包含主键和索引字段以及其它字段,测试结果为使用索引

explain SELECT sid,sname,sage from test_student WHERE sname like '张%';

使用where条件限定要查询的数据避免返回多余的行

比如查询某个用户是否是会员

反例:

List<Long> userIds = sqlMap.queryList("select userId from user where isVip=1");boolean isVip = userIds.contains(userId);

正例:

Long userId = sqlMap.queryObject("select userId from user where userId='userId' and isVip='1' ")boolean isVip = userId!=null;

理由:

需要什么数据,就去查什么数据,避免返回不必要的数据,节省开销。

尽量避免在索引列上使用mysql内置的函数

业务需求:

查询最近七天内登陆过的⽤户(假设loginTime加了索引)

反例:

select userId,loginTime from loginuser where Date_ADD(loginTime,Interval 7 DAY) >=now();

正例:

select userId,loginTime from loginuser where loginTime >= Date_ADD(NOW(),INTERVAL - 7DAY);

理由:

索引列上使用Mysql的内置函数,会导致索引失效

失效测试

explain SELECT sid,sname,sage from test_student WHERE DATE_ADD(sdate,INTERVAL 7 DAY)>=NOW();

不失效测试

explain SELECT sid,sname,sage from test_student WHERE sdate >=DATE_ADD(NOW(),INTERVAL -7 DAY);

应尽量避免在where子句中对字段进行表达式操作,这将导致系统放弃使用索引而进行全表扫描

在sage上添加索引

CREATE INDEX age_index on test_student(sage)

反例:

explain SELECT sid,sname,sage from test_student WHERE sage-1 = 10

正例:

explain SELECT sid,sname,sage from test_student WHERE sage = 11

Inner join 、left join、right join,优先使⽤Inner join,如果是left join,左边表结果尽量小

Inner join 内连接,在两张表进⾏连接查询时,只保留两张表中完全匹配的结果集

left join 在两张表进⾏连接查询时,会返回左表所有的⾏,即使在右表中没有匹配的记录。

right join 在两张表进⾏连接查询时,会返回右表所有的⾏,即使在左表中没有匹配的记录。

都满足SQL需求的前提下,推荐优先使⽤Inner join(内连接),如果要使⽤left join,左边表数据结果尽量小,如果有条件的尽量放到左边处理。

反例:

select * from tab1 t1 left join tab2 t2 on t1.size = t2.size where t1.id>2;

正例:

select * from (select * from tab1 where id >2) t1 left join tab2 t2 on t1.size = t2.size;

理由:

如果inner join是等值连接,或许返回的⾏数⽐较少,所以性能相对会好⼀点。

同理,使⽤了左连接,左边表数据结果尽量小,条件尽量放到左边处理,意味着返回的⾏数可能⽐较少。

应尽量避免在where子句中使用!=或<>操作符,否则引擎将放弃使用索引而进行全表扫描

反例:

explain SELECT sid,sname,sage from test_student WHERE sage <> 11

使用联合索引时,注意索引列的顺序,⼀般遵循最左匹配原则

组合索引是在多个字段上创建一个索引。

CREATE INDEX group_index on fruit(id,name,price);

可以看到如果查询时包含了id则使用了索引,否则不使用索引

EXPLAIN SELECT * FROM fruit WHERE id='1' AND city = '北京';
EXPLAIN SELECT * FROM fruit WHERE name='苹果' AND city = '北京';

理由:

当我们创建⼀个联合索引的时候,如(k1,k2,k3),相当于创建了(k1)、(k1,k2)和(k1,k2,k3)三个索引,

这就是最左匹配原则。

联合索引不满⾜最左原则,索引⼀般会失效,但是这个还跟Mysql优化器有关的。

对查询进行优化,应考虑在where及order by涉及的列上建立索引,尽量避免全表扫描

见文知意,不再举例。

如果插入数据过多,考虑批量插入

参考如下

SpringBoot+MybatisPlus+Mysql实现批量插入万级数据多种方式与耗时对比:

SpringBoot+MybatisPlus+Mysql实现批量插入万级数据多种方式与耗时对比_mybaytis-plus大数据批量插入-CSDN博客

在适当的时候,使用覆盖索引

覆盖索引能够使得你的SQL语句不需要回表,仅仅访问索引就能够得到所有需要的数据,⼤⼤提⾼了查询效率。

覆盖索引(Covering Index)是指一个查询操作中,数据库可以通过遍历索引而不需要访问数据行就可以返回查询所需要的数据的情况。

这样可以减少磁盘I/O,提高查询效率。

在MySQL中,要创建覆盖索引,需要确保查询中的所有列都是索引的一部分,并且索引的顺序尽可能地匹配查询中列的顺序。

例如,假设有一个表users,它有以下列和索引:

CREATE TABLE test_users (id INT NOT NULL AUTO_INCREMENT,first_name VARCHAR(50),last_name VARCHAR(50),email VARCHAR(100),PRIMARY KEY (id)
);CREATE INDEX idx_name ON test_users (first_name, last_name);

如果你想要查询用户的first_name和last_name,并且你知道这些信息将用于很多查询,你可以创建一个覆盖索引

CREATE INDEX idx_covering ON test_users (first_name, last_name);

这样,当你执行一个只需要这些列的查询时

SELECT first_name, last_name FROM test_users WHERE first_name = 'John';

MySQL可以通过扫描索引idx_covering而不是查询数据行来提供结果,从而提高查询效率。

慎用distinct关键字

distinct 关键字⼀般⽤来过滤重复记录,以返回不重复的记录。

在查询⼀个字段或者很少字段的情况下使用时,给查询带来优化效果。

但是在字段很多的时候使⽤,却会大大降低查询效率。

反例:

SELECT DISTINCT * from user;

正例:

select DISTINCT name from user;

理由:

带distinct的语句cpu时间和占⽤时间都⾼于不带distinct的语句。

因为当查询很多字段时,如果使⽤distinct,数据库引擎就会对数据进⾏⽐较,过滤掉重复数据,

然⽽这个⽐较、过滤的过程会占⽤系统资源,cpu时间。

删除冗余和重复索引

反例:

KEY 'idx_userId' ('userId') KEY 'idx_userId_age' ('userId','age')

正例:

删除userId索引,因为组合索引(A,B)相当于创建了(A)和(A,B)索引。

理由:

重复的索引需要维护,并且优化器在优化查询的时候也需要逐个地进行考虑,这会影响性能的。

如果数据量较大,优化你的修改/删除语句

避免同时修改或删除过多数据,因为会造成cpu利⽤率过⾼,从而影响别人对数据库的访问。

⼀次性删除太多数据,可能会有lock wait timeout exceed的错误,所以建议分批操作。

where子句中考虑使用默认值代替null

反例:

select * from user where age is not null;

正例:

select * from user where age>0;

设置age列0为默认值。

理由:

并不是说使⽤了is null 或者 is not null 就会不⾛索引了,这个跟mysql版本以及查询成本都有关。

如果mysql优化器发现,⾛索引比不⾛索引成本还要⾼,肯定会放弃索引,这些条件 !=,isnull,isnotnull 经常被认为让索引失效,

其实是因为⼀般情况下,查询的成本⾼,优化器⾃动放弃索引的。

如果把null值,换成默认值,很多时候让⾛索引成为可能,同时,表达意思会相对清晰⼀点。

不要有超过5个以上的表连接

连表越多,编译的时间和开销也就越⼤。

把连接表拆开成较小的几个执行,可读性更⾼。

如果⼀定需要连接很多表才能得到数据,那么意味着糟糕的设计了

exist&in的合理利用

假设表A表示某企业的员⼯表,表B表示部门表,查询所有部⻔的所有员⼯,很容易有以下SQL

 select * from A where deptId in (select deptId from B);

这样写等价于:

先查询部门表B

select deptId from B

再由部门deptId,查询A的员⼯

select * from A where A.deptId = B.deptId

除了使用in,我们也可以使用exist实现一样的查询功能

select * from A where exists (select 1 from B where A.deptId = B.deptId);

因为exists查询的理解就是,先执⾏主查询,获得数据后,再放到子查询中做条件验证,根据验证结果(true或者false),

来决定主查询的数据结果是否保留。

那么,这样写就等价于

select * from A,先从A表做循环

select * from B where A.deptId = B.deptId,再从B表做循环.

数据库最费劲的就是跟程序链接释放。

假设链接了两次,每次做上百万次的数据集查询,查完就⾛,这样就只做了两次;

相反建⽴了上百万次链接,申请链接释放反复重复,这样系统就受不了了。

即mysql优化原则,就是小表驱动大表,小的数据集驱动大的数据集,从而让性能更优。

因此,我们要选择最外层循环小的,也就是,如果B的数据量小于A,适合使⽤in,

如果B的数据量大于A,即适合选择exist。

尽量使用union all 替换union

如果检索结果中不会有重复的记录,推荐union all 替换 union

反例:

select * from user where userid=1 union select * from user where age = 10

正例:

select * from user where userid=1 union all select * from user where age = 10

理由:

如果使用union,不管检索结果有没有重复,都会尝试进⾏合并,然后在输出最终结果前进行排序。

如果已知检索结果没有重复记录,使⽤union all 代替union,这样会提⾼效率。

索引不宜太多,一般5个以内

索引并不是越多越好,索引虽然提⾼了查询的效率,但是也降低了插⼊和更新的效率。

insert或update时有可能会重建索引,所以建索引需要慎重考虑,视具体情况来定。

⼀个表的索引数最好不要超过5个,若太多需要考虑⼀些索引是否没有存在的必要。

尽量使用数字型字段,若只含数值信息的字段尽量不要设计为字符型

理由:

相对于数字型字段,字符型会降低查询和连接的性能,并会增加存储开销。

另外,参考文首博客踩坑。

索引不适合建在有大量重复数据的字段上,如性别这类型数据库字段

因为SQL优化器是根据表中数据量来进⾏查询优化的,如果索引列有⼤量重复数据,

Mysql查询优化器推算发现不走索引的成本更低,很可能就放弃索引了。

尽量避免向客户端返回过多数据量

分页实现。

当在SQL语句中连接多个表时,请使⽤表的别名,并把别名前缀于每⼀列上,这样语义更加清晰

规范使用表别名。

尽可能使用varchar/nvarchar代替char/nchar

理由:

因为⾸先变长字段存储空间小,可以节省存储空间。

其次对于查询来说,在⼀个相对较小的字段内搜索,效率更高。

为了提⾼group by语句的效率,可以在执⾏到该语句前,把不需要的记录过滤掉

反例:

select job,avg(salary) from employee group by job having job ='president' or job = 'managent'

正例:

select job,avg(salary) from employee where job ='president' or job= 'managent' group by job;

如果字段类型是字符串,where时⼀定用引号括起来,否则索引失效

反例:

explain select * from test_student where eid = 1;

正例:

explain select * from test_student where eid = '1';

理由:

因为不加单引号时,是字符串跟数字的⽐较,它们类型不匹配,MySQL会做隐式的类型转换,

把它们转换为浮点数再做比较。

使用explain分析你SQL的计划

Mysql中索引的分类、增删改查与存储引擎对应关系:

Mysql中索引的分类、增删改查与存储引擎对应关系-CSDN博客

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

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

相关文章

Java面试题--JVM大厂篇之深入解析JVM中的Serial GC:工作原理与代际区别

目录 引言&#xff1a; 正文&#xff1a; 一、Serial GC工作原理 年轻代垃圾回收&#xff08;Minor GC&#xff09;&#xff1a; 老年代垃圾回收&#xff08;Major GC或Full GC&#xff09;&#xff1a; 二、年轻代和老年代的区别 年轻代&#xff08;Young Generation&a…

docker快速安装(环境CentOS7)

1. 查看自己的Linux系统 cat /etc/redhat-release 2. 安装依赖插件 yum -y install gcc yum -y install gcc-c yum install -y yum-utils yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum makecache fast yum -y insta…

MongoDB常用命令大全,概述、备份恢复

文章目录 一、MongoDB简介二、服务启动停止、连接三、数据库相关四、集合操作五、文档操作六、数据备份与恢复/导入导出数据6.1 mongodump备份数据库6.2 mongorestore还原数据库6.3 mongoexport导出表 或 表中部分字段6.4 mongoimport导入表 或 表中部分字段 七、其他常用命令八…

养猪管理如何实现远程监控

在现代化农业快速发展的背景下&#xff0c;养猪管理的智能化与远程监控技术的应用日益成为提升养殖效率、保障动物健康及优化资源配置的关键手段。实现养猪管理的远程监控&#xff0c;不仅能够实时掌握猪场环境参数与生猪生长状况&#xff0c;还能有效预防疾病、提高生产性能&a…

Spring Cloud环境搭建

&#x1f3a5; 个人主页&#xff1a;Dikz12&#x1f525;个人专栏&#xff1a;Spring学习之路&#x1f4d5;格言&#xff1a;吾愚多不敏&#xff0c;而愿加学欢迎大家&#x1f44d;点赞✍评论⭐收藏 目录 1. 开发环境安装 1.1 安装JDK ​1.2 安装MySQL 2. 案列介绍 2.1 …

C语言指针超详解——进阶篇

C语言指针系列文章目录 入门篇 强化篇 进阶篇 文章目录 C语言指针系列文章目录1. 字符指针变量2. 数组指针变量2. 1 概念2. 2 数组指针变量的初始化 3. 二维数组传参的本质4. 函数指针变量4. 1 函数指针变量的创建4. 2 指针变量的使用4. 3 两个有趣的代码4. 3. 1 代码一4. 3. …

汽车底盘控制系统Autosar初步接触

最近接触到汽车底盘控制部分&#xff0c;作为小白&#xff0c;原以为汽车底盘也是要自己手敲代码&#xff0c;结果发现完全不是。记录一下最近的学习心得&#xff0c;初步接触东西不全&#xff0c;但可以当作参考。 对于底盘控制部分的简单理解&#xff1a;simulink做汽车底盘的…

大数据技术基础

一、大数据平台 1.大数据平台方案步骤&#xff1a; ①市场上有哪些大数据平台 ②硬件、系统、业务增长等方面 ③方案是否通过 通过后&#xff1a;按照一期目标投入 先虚拟环境部署联系&#xff0c;再实际部署 《大数据架构介绍》《Hadoop架构解析》《Hadoop集群规划》 《H…

PX4 运行 make px4_sitl_default gazebo 报错

报错原因&#xff1a;最开始我把依赖一直都是在base环境下安装的&#xff0c;没有conda deactivate&#xff0c;而pip install的东西应该装在系统环境&#xff0c;不能装在base环境下&#xff0c;sudo apt 是装在系统环境的 1.检查ros 用鱼香ros安装 wget http://fishros.…

南平建网站公司推荐 好用的b2b独立站模板

床品毛巾wordpress独立站模板 床单、被套、毛巾、抱枕、靠垫、围巾、布艺、枕头、乳胶枕、四件套、浴巾wordpress网站模板。 https://www.jianzhanpress.com/?p4065 打印耗材wordpress自建独立站模板 色带、墨盒、碳粉、打印纸、硒鼓、墨盒、墨水、3D打印机、喷头wordpress…

Pr 2024下载安装,Adobe Premiere pro2024剪辑软件下载合集获取

Premiere Pro 2023中文版简称Pr&#xff0c;pr2023是一款视频编辑软件。 pr 2023不仅可以帮助用户对各种视频进行剪辑、旋转、分割、合并、字幕添加、背景音乐等基础的处理&#xff0c;还能帮助用户进行视频颜色校正、颜色分级、稳定镜头、调整层、更改片段的持续时间和速度、效…

Sentinel规则持久化Push模式两种实现方式

文章目录 sentinel持久化push推模式微服务端的实现具体实现源码分析读数据源写数据源的实现 微服务端解析读数据源流程 修改源码的实现官方demo修改源码实现配置类flowauthoritydegreadparamsystemgateway修改源码 测试补充 前置知识 pull模式 sentinel持久化push推模式 pull拉…

Pycharm 导入 conda 环境

使用时经常在此处卡壳&#xff0c;在此做个记录。 这个位置选择 conda 安装路径下的 python.exe 文件即可

自学鸿蒙HarmonyOS的ArkTS语言<十>@BuilderParam装饰器

作用&#xff1a;当子组件多处使用时&#xff0c;给某处的子组件添加特定功能 一、初始化 1、只能被Builder装饰的方法初始化 2、使用所属自定义组件的builder方法初始化 3、使用父组件的builder方法初始化 - 把父组件的builder传过去&#xff0c;参数名和子组件的builderPar…

ESP32部署TensorFlow Lite

本来是想找一篇中文教程&#xff0c;不过只看到一个英文官方的&#xff0c;也行吧&#xff0c;虽然效率会慢丢丢。 GitHub - espressif/esp-tflite-micro: TensorFlow Lite Micro for Espressif Chipsets 看了一圈&#xff0c;有个中文的&#xff1a; esp-dl/README_cn.md a…

TS 入门(七):TypeScript模块与命名空间

目录 前言回顾泛型编程1. 模块a. 导入和导出b. 默认导出c. 重命名导入和导出 2. 命名空间a. 定义命名空间b. 嵌套命名空间 3. 动态导入与条件导入a. 动态导入b. 条件导入 结语 前言 在前几章中&#xff0c;我们学习了 TypeScript 的基础知识、函数与对象类型、接口与类、以及泛…

K8S 上部署 Emqx

文章目录 安装方式一&#xff1a;快速部署安装方式二&#xff1a;定制化部署1. 使用 Pod 直接部署 EMQX Broker2. 使用 Deoloyment 部署 Pod3. 使用 Services 公开 EMQX Broker Pod 服务4. 通过 kubernetes 自动集群 EMQX MQTT 服务器5. 修改 EMQX Broker 的配置 安装方式一&am…

Large Language Model系列之二:Transformers和预训练语言模型

Large Language Model系列之二&#xff1a;Transformers和预训练语言模型 1 Transformer模型 Transformer模型是一种基于自注意力机制的深度学习模型&#xff0c;它最初由Vaswani等人在2017年的论文《Attention Is All You Need》中提出&#xff0c;主要用于机器翻译任务。随…

【ollama】ollama运行GLM4-9B和CodeGeeX4-ALL-9B

一、下载GGUF模型 glm-4-9b-chat-GGUFcodegeex4-all-9b-GGUF 使用modelscope下载 先安装 pip install modelscope 命令1 modelscope download --modelLLM-Research/glm-4-9b-chat-GGUF --local_dir . glm-4-9b-chat.Q5_K.gguf命令2 modelscope download --modelLLM-Researc…

昇思25天学习打卡营第02天|张量 Tensor

一、什么是张量 Tensor 张量是一种特殊的数据结构&#xff0c;与数组和矩阵非常相似。张量&#xff08;Tensor&#xff09;是MindSpore网络运算中的基本数据结构。 张量可以被看作是一个多维数组&#xff0c;但它比普通的数组更加灵活和强大&#xff0c;因为它支持在GPU等加速…