SQL优化

SQL优化是每个开发人员在日常工作中都要面对的任务。一个性能优良的SQL语句可以非常有效的提高数据库的响应速度,减少系统资源消耗。本篇文章将分享一些SQL优化的小技巧,帮助你轻松应对各种SQL性能问题。

1.避免不必要的列

查询时尽量避免查询冗余字段,不要使用select * ,只查询要用到的列,减少数据传输量

  • 减少I/O开销 :数据库在查询时,每读取一个数据块(通常包含多个行),都需要从磁盘上读取数据。当你使用SELECT *时,数据库会读取整行的数据,即使你的应用只需要其中的一部分字段。这增加了不必要的I/O开销。
  • 减少网络传输:在分布式系统中,数据库和应用服务器可能位于不同的物理位置。当你查询大量数据时,减少返回的数据量可以显著减少网络传输的开销。
  • 明确性:只选择需要的列可以使查询的意图更加明确,有利于其他开发人员理解查询的目的。
  • 兼容性 :如果数据库结构在未来发生变化(例如,某个字段被删除或重命名),使用SELECT *的查询可能会失败,而明确指定字段的查询则不会。

2.分页优化

当数据量很大的时候,limit和offset可能会导致性能问题,因为数据库查询时需要扫描offset+limit数量的行,可以考虑延迟关联(Late Row Lookups)和(Seek MEthod)这两种方法优化分页查询

延迟关联(Late Row Lookups)

  • 原理 :首先,通过索引快速定位到需要的数据行的位置,然后再去获取这些行的完整数据。这可以避免使用LIMITOFFSET时导致的全表扫描或大范围扫描。
  • 实现:通常,你可以通过一个子查询来定位到需要的数据行的主键,然后再根据这些主键去获取完整的数据。

Seek Method(或Keyset Pagination)

  • 原理 :与传统的基于偏移量的分页不同,Seek Method 是基于上一页最后一行的某个字段(通常是主键或具有唯一性的字段)的值来进行分页的。这种方法可以避免在数据量很大时OFFSET导致的性能问题。
  • 实现 :在你的应用中,保存上一页最后一行的某个字段的值。在下一页的查询中,使用这个值作为起始点来获取数据。例如,如果你使用主键作为起始点,那么查询可能类似于 WHERE id > :last_id LIMIT :page_size
  • 优点:随着翻页的进行,查询的效率不会降低,因为数据库总是从上次查询的结束点开始查找。
  • 缺点:不能随意地跳转到任意一页,只能一页一页地往后翻。同时,如果删除或修改了数据,可能会导致"跳过"某些行或重复显示某些行的问题。为了解决这个问题,你可能需要定期重新计算或更新你的起始点值。

3.索引优化

正确使用索引可以非常有效的减少sql查询时间,通常我们可以从索引覆盖,尽量避免用!=或者<>操作符、适当地去使用前缀索引、避免使用列上函数运算、正确使用联合索引等方面进行优化。

(1).使用索引覆盖

在使用非主键索引查询数据的时候如果查询的列有未在索引上的列,则需要回表查询,但是如果索引覆盖了,那么就不需要回表查询了

(2).避免使用!=或者<>操作符,尽量修改成使用=、>、<、BETWEEN等操作符

如果使用!=或者<>操作符会导致索引失效,从而引发全表扫描,比如说可以把class<>2,修改成class>2 or class< 2,就可以使用索引了

(3).适当使用前缀索引

(4).避免在列上使用函数

在where子句中直接对列使用函数会导致索引失效,比如说date_format,因为数据库需要对每行的列应用函数后再进行比较,无法直接利用索引

(5).正确使用联合索引

正确的使用联合索引可以大大提高查询效率,联合索引的创建应该符合最左前缀原则,就是索引的顺序应该根据列在查询中的使用频率和重要性来安排

(6)一张表中索引不要建立太多,一般5个以内

  • 索引不是越多越好,虽然提高查询效率,但是会降低插入和更新的效率,插入和更新的时候有可能会导致重建索引,如果数据量巨大,重建将会对记录进行重新排序
  • 创建索引时,数据库会为每个索引分配磁盘空间。索引过多会占用大量的磁盘空间,可能导致磁盘空间不足,影响数据库的正常运行。
  • 虽然索引能够提高查询效率,但索引过多也可能导致查询效率下降。因为查询时需要扫描所有的索引,而索引过多会导致扫描的时间变长,从而降低查询效率。

4.不要使用join关联太多表,最多不要超过3张

<<阿里巴巴Java开发手册>>上提出join关联最多不要超过3张表,因为join太多表会进行笛卡尔积生成大量数据降低查询速度,可以考虑将复杂的JOIN查询分解为多个简单查询,然后再内存中去组合查询结果。

5.对排序进行优化

MySQL生成有序结果的方式有两种,一种是对结果集进行排序操作,另外一种是根据索引顺序得出的自然有序结果 ,所以在设计索引的时候需要充分考虑到排序的需求。

6.对UNION进行优化

UNION在SQL中用于合并两个或多个SELECT语句的结果集,但合并的结果集不包含重复行。UNION ALL则包括所有行,包括重复行。在使用UNION时,需要注意以下几点以进行优化:

  1. 减少结果集的大小:由于UNION会去除重复行,因此在合并结果集之前,每个SELECT语句都应该尽可能减少返回的行数。这可以通过在WHERE子句中增加限制条件或使用LIMIT子句来实现。

  2. 确保索引有效:每个SELECT语句都应该尽可能使用索引来加快查询速度。当使用UNION时,每个SELECT语句都会单独执行,并且每个查询都可以使用其自己的索引。

  3. 排序和LIMIT:如果需要对UNION的结果进行排序,最好是在最后一个SELECT语句中进行,而不是在UNION操作后对整个结果集进行排序。同样,如果需要限制返回的行数,也应该在最后一个SELECT语句中使用LIMIT子句。

  4. 使用UNION ALL代替UNION:如果确定合并的结果集中不会有重复行,或者不介意结果集中的重复行,那么应该使用UNION ALL代替UNION。因为UNION ALL不需要去除重复行,所以它的执行速度通常比UNION快。

  5. 避免在UNION中使用ORDER BY:在UNION的每个子查询中使用ORDER BY通常是没有意义的,因为UNION会打乱这些顺序。如果需要对最终的结果集进行排序,应该在UNION操作后对整个结果集使用ORDER BY。

  6. 优化子查询:如果UNION中的每个SELECT语句都是一个复杂的子查询,那么应该考虑优化这些子查询以提高性能。这包括使用索引、减少数据转换、避免在WHERE子句中使用OR等。

  7. 考虑使用临时表:如果UNION操作涉及大量数据或复杂的逻辑,并且需要频繁执行,那么可以考虑将结果集存储在一个临时表中,然后对该临时表进行查询。这可以避免重复执行UNION操作,从而提高性能。

7.Where子句优化

谨慎在where子句使用or来连接条件

WHERE子句中使用OR操作符时,可能会导致索引失效,当WHERE子句中的OR条件导致索引使用不高效时,**可以考虑将查询拆分为多个部分,每个部分使用单独的索引,并使用UNIONUNION ALL来合并结果,**如下所示:

SELECT * FROM employees WHERE salary > 50000 OR department = 'IT';

这个查询可能会因为OR而导致索引使用不高效,如果employees表有一个复合索引(例如,在salarydepartment上),并且查询优化器能够高效地使用它,那么原始的OR查询可能仍然是一个好的选择。作为替代方案,我们可以使用UNION(去除重复行)或UNION ALL(包括所有行,包括重复行)来重写查询:

(SELECT * FROM employees WHERE salary > 50000)  
UNION  
(SELECT * FROM employees WHERE department = 'IT');

以下是为什么OR可能导致索引失效的几个原因:

索引选择复杂性

WHERE子句中包含OR时,数据库需要评估多个条件,并确定如何最有效地检索数据。如果每个OR条件都对应于一个单独的索引,那么数据库可能需要决定是使用哪个索引,还是根本不使用索引而进行全表扫描。由于这种选择的复杂性,数据库可能会选择不使用任何索引,从而导致索引失效。

索引不匹配

如果OR条件中的某个字段没有索引,或者该索引不适用于当前查询,那么即使其他条件有合适的索引,整个WHERE子句也可能不会使用索引。这是因为数据库需要同时考虑所有条件,而不仅仅是那些有索引的条件。(比如说上述例子中如果department字段没有索引,那么尽管salary走了索引,而到department查询条件时,还是会进行全表扫描。也就是说整个过程需要三步走,索引扫描+全表扫描+合并)

8.尽量避免进行类型转换

错误的写法(可能导致索引失效)

SELECT * FROM students WHERE name = 123; -- 这里123是数字,而不是字符串

在这个例子中,MySQL会尝试将name列的值转换为数字类型来与123进行比较,这通常会导致全表扫描,因为索引(如果存在)是基于字符串类型构建的。

正确的写法(利用索引)

SELECT * FROM students WHERE name = 'John'; -- 这里'John'是字符串

在这个例子中,因为两边都是字符串类型,所以MySQL可以直接使用基于name列的索引(如果存在的话)来快速定位到相应的行。

9.提高group by 的效率

先对数据进行过滤再进行分组,把不必要的数据进行过滤

相关推荐
TDengine (老段)26 分钟前
两分钟掌握 TDengine 全部写入方式
大数据·数据库·时序数据库·tdengine·涛思数据
码农君莫笑40 分钟前
《信管通低代码信息管理系统开发平台》Windows环境安装说明
服务器·数据库·windows·低代码·c#·bootstrap·.netcore
计算机学长felix1 小时前
基于SpringBoot的“大学生社团活动平台”的设计与实现(源码+数据库+文档+PPT)
数据库·spring boot·后端
木与子不厌1 小时前
微服务自定义过滤器
运维·数据库·微服务
派可数据BI可视化1 小时前
连锁餐饮行业数据可视化分析方案
大数据·数据库·数据仓库·数据分析·商业智能bi
dbcat官方1 小时前
1.微服务灰度发布(方案设计)
java·数据库·分布式·微服务·中间件·架构
青年有志1 小时前
深入浅出 MyBatis | CRUD 操作、配置解析
数据库·tomcat·mybatis
数据的世界011 小时前
SQL创建和操纵表
数据库·sql
Echo flower2 小时前
mybatis-plus自动填充时间的配置类实现
java·数据库·mybatis