SQL慢查询优化方式

目录

一、SQL语句优化

1.避免使用 SELECT * ,而是具体字段

2.避免使用 % 开头的 LIKE 的查询

3.避免使用子查询,使用JOIN

4.使用EXISTS代替IN

5.使用LIMIT 1优化查询

6.使用批量插入、优化INSERT操作

7.其他方式

二、SQL索引优化

1.在查询条件或者连接条件的列上建立索引

2.遵循最左前缀原则

3.避免在索引列上进行计算

4.使用覆盖索引

5.避免使用更新频繁的列作为索引

6.避免过多的列使用复合索引

7.定期维护索引

三、EXPLAIN分析查询


SQL优化一般从SQL语句开始优化,再分析索引,即使有了良好的索引,糟糕的查询语句也可能导致性能问题

优化查询语句可以帮助数据库更有效地利用现有的资源,减少不必要的开销。关于如何从查询语句开始优化的步骤:

  1. 识别慢查询
  2. SQL优化
  3. 索引使用
  4. 使用 EXPLAIN 分析查询

一、SQL语句优化

不合理的SQL语句会导致:

  • 索引失效,无法使用索引
  • 全表扫描,因为数据库必须检查每一行数据以确定是否匹配
  • 对于大型表,这会导致性能问题资源消耗

下面的例子🌰以User表为例

CREATE TABLE User (
    user_id INT PRIMARY KEY,
    name VARCHAR(255),
    phone VARCHAR(20),
    email VARCHAR(255),
    role VARCHAR(50),
    address VARCHAR(255),
    birthday DATETIME
);

1.避免使用 SELECT * ,而是具体字段

❌:索引失效,全表扫描

SELECT * FROM user

✅:节省资源、减少网络开销。可能用到覆盖索引,减少回表,提高查询效率

SELECT id,username,sex FROM user

📌原因:

  • 只取实际需要的字段,节省资源、减少网络开销。

  • 可能用到覆盖索引,减少回表,提高查询效率

覆盖索引和回表下文会详解,此处不做过多赘述

⚠️注意:为节省时间,下面的样例字段都用 SELECT * 代替了

2.避免使用 % 开头的 LIKE 的查询

❌:由于 % 表示任意数量的字符,数据库无法利用索引来加速查询,因为它不知道从索引树的哪个位置开始搜索,从而导致全表扫描,性能较差,特别是在大表上

SELECT * FROM User WHERE name LIKE '%echola';

✅:可以利用 name 列上的索引来快速定位以 'echola' 开始的所有用户名,也就是从索引树的特定节点开始搜索,而不是从根节点遍历整个树

SELECT * FROM User WHERE name LIKE 'echola%';

3.避免使用子查询,使用JOIN

❌:子查询可能会针对每个外部查询的结果行多次执行,无法有效使用索引,可能需要创建临时表来存储中间结果

SELECT * FROM User 
WHERE id IN (
    SELECT user_id 
    FROM Orders 
    WHERE status = 'completed');

✅:JOIN 只需执行一次,可以更好地利用索引,通常不需要创建临时表

SELECT u.* 
FROM User u JOIN Orders o ON u.id = o.user_id 
WHERE o.status = 'completed';

inner joinleft joinright join优先使用inner join

   三种连接如果结果相同,优先使用inner join(返回行数少),如果使用left join左边表尽量小(小表在前,大表在后)

  • inner join 内连接,只保留两张表中完全匹配的结果集;

  • left join会返回左表所有的行,即使在右表中没有匹配的记录;

  • right join会返回右表所有的行,即使在左表中没有匹配的记录

⚠️:表连接不宜太多,一般5个以内

4.使用EXISTS代替IN

虽然需避免子查询,但是某些情况下还是需要使用子查询,使用EXISTS代替IN可以提高查询效率

❌:IN 会遍历整个子查询结果集

SELECT * FROM User 
WHERE id IN (
    SELECT user_id 
    FROM Orders 
    WHERE status = 'completed');

✅:EXISTS 找到第一个匹配项后就会停止搜索

SELECT * FROM User 
WHERE id EXISTS (
    SELECT user_id 
    FROM Orders 
    WHERE status = 'completed');

不适合使用 EXISTS 的情况:EXISTS 只能返回一个布尔值,无法返回具体的子查询结果

❌:需要返回每个用户的最新订单信息,包括 order_id 和 order_date,EXISTS 无法达到目的

SELECT u.id, u.name, o.order_id, o.status, o.order_date 
FROM User u 
WHERE EXISTS (
    SELECT 1 
    FROM Orders o 
    WHERE o.user_id = u.id 
    AND o.status = 'completed' 
    AND o.order_date = '2024-08-22'
);

✅:JOIN 可以返回具体的子查询结果,包括每个用户的订单信息

SELECT u.id, u.name, o.order_id, o.status, o.order_date 
FROM User u 
JOIN (
    SELECT user_id, order_id, status, order_date 
    FROM Orders 
    WHERE status = 'completed' 
    AND order_date ='2024-08-22'
) latest_orders ON u.id = latest_orders.user_id;

5.使用LIMIT 1优化查询

在只需要一条结果的查询中使用 LIMIT 1 可以提高性能,

尤其是在使用Mybatis Plus中的selectOne(),若有多条数据符合,则会抛出异常,因此需要添加 LIMIT 1

SELECT * FROM users WHERE name = 'echola' LIMIT 1;

6.使用批量插入、优化INSERT操作

使用批量插入可以减少事务提交次数:在批量插入过程中,尽量减少事务提交的次数。可以在一个事务中插入多条记录,然后一次性提交。在开发中一般使用MybatisPlus的saveBatch()

START TRANSACTION;
INSERT INTO User (user_id, name, phone, email, role, address, birthday)
VALUES
       (1, 'Alice', '1234567890', 'alice@example.com', 'user', 'New York', '2000-01-01 00:00:00'),
       (2, 'Bob', '0987654321', 'bob@example.com', 'admin', 'Los Angeles', '1999-01-01 00:00:00'),
       (3, 'Charlie', '5555555555', 'charlie@example.com', 'user', 'Chicago', '2001-01-01 00:00:00');
COMMIT;
   

7.其他方式

避免在 WHERE 子句中使用 OR 来连接条件

❌:使用 OR 连接多个条件可能导致索引失效,从而导致全表扫描

SELECT * FROM User WHERE age = 25 OR name = 'echola';

✅:为了更好利用索引,可以将查询拆分为两个独立的子查询,并使用 UNION ALL 合并结果:

SELECT * FROM User WHERE age = 25
UNION ALL
SELECT * FROM User WHERE name = 'echola';

避免使用 NOT IN、!=、<> 等负条件,因为这些条件不能有效使用索引 

避免在索引列上使用IS NULLIS NOT NULL

避免使用HAVING代替WHERE,在可能的情况下,使用 WHERE 代替 HAVING 进行过滤,因为 HAVING 是在聚合之后进行过滤,性能较差

二、SQL索引优化

合理地使用索引是SQL最重要的

如果数据量小的表,可以不建立索引,但数据量大的表肯定是需要建立索引的

如果一张表上kw的数据量,果索引使用不当,也可能会导致索引失效,反而成为负担

以下关键词条解释:

复合索引:多个列组合成的索引

覆盖索引:索引中包含了查询所需的全部字段
最左前缀原则:在复合索引中,查询条件应尽可能地按照索引的顺序进行匹配

回表:当数据库使用索引来加速查询时,如果索引是非聚集索引(也称为辅助索引或者二级索引),那么索引项中存储的并不是完整的行数据,而是指向实际数据行的指针。当数据库通过索引查找到匹配的索引项后,还需要根据这个指针回到实际的数据页去获取完整的行数据

1.在查询条件或者连接条件的列上建立索引

⚠️:只要发现接口查询缓慢,优先检查 WHERE 后面的条件,有没有创建索引,如果已经建立索引,需要创建复合索引,调整现有索引

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

CREATE INDEX idx_userid ON User(user_id);
SELECT * FROM User WHERE user_id = 123 AND name = 'echola';

2.遵循最左前缀原则

对于复合索引(name, age, phone),可以用于(name),(name, age),(name, age, phone)顺序匹配查询

CREATE INDEX idx_name_age_phone ON User(name, age, phone);

❌:此处是(age,phone),没有按照索引的顺序进行匹配 ,导致索引无法完全利用,可能需要额外的回表操作

SELECT * FROM User WHERE age = 25 AND phone = '1234567890';

✅:按顺序匹配(name, age)

SELECT * FROM User WHERE phone = '1234567890' AND age = 25 ;

3.避免在索引列上进行计算

计算包含:算术运算、字符串函数、日期函数、类型转换、其他函数,不注意就会导致索引失效

字符串函数:CONCAT、SUBSTRING、UPPER、REPLACE

日期时间函数:DATE_ADD、DATE_FORMAT、UNIX_TIMESTAMP

类型转换:STR_TO_DATE、CAST

其他函数:

  • 条件表达式:CASE WHEN age > 30 THEN 'adult' ELSE 'child' END
  • 其他内置函数:MD5(password), SHA1(password)

❌:只是对date加了索引,而YEAR(date)无索引,从而无法使用索引,导致全表扫描

CREATE INDEX idx_birthday ON User(birthday);

SELECT * FROM User WHERE YEAR(birthday) = 2024;

✅:

SELECT * FROM User WHERE birthday BETWEEN '2024-01-01' AND '2024-12-31';

4.使用覆盖索引

如果查询的所有列都在索引中,那么数据库引擎可以不访问表中的数据而直接从索引中获取所需信息,这样可以减少回表操作,从而提高查询效率

❌:不使用覆盖索引

CREATE INDEX idx_userid ON User(user_id);

SELECT user_id, phone, role FROM User WHERE user_id = 123;

索引 idx_userid 只包含 user_id 列,但 user_id, phone, 和 role 列不在索引里,导致需要回表访问主键索引或其他索引

✅:索引中覆盖了所有查询的列,数据库可以直接从索引中获取数据

CREATE INDEX idx_userid_phone_role ON User(user_id, phone, role);

SELECT user_id, phone, role FROM User WHERE user_id = 123;

5.避免使用更新频繁的列作为索引

更新频繁的列作为索引会导致较高的维护成本,降低查询性能

❌:phone 列更新频繁,每次更新都会导致索引的重建,更新成本高,影响性能

CREATE INDEX idx_phone ON User(phone);

6.避免过多的列使用复合索引

复合索引的列数不要太多,列数过多会增加索引的维护开销,并且可能导致索引文件过大。对此可以拆分为较少复合索引和单个索引

CREATE INDEX idx_userid_phone_role_address ON User(user_id, phone, role, address);

7.定期维护索引

使用 ANALYZE TABLE 更新统计信息,并使用 OPTIMIZE TABLE 来整理碎片化的索引 ,特别是在大量插入、更新和删除操作后,表可能会出现碎片化,导致性能下降

ANALYZE TABLE User;
OPTIMIZE TABLE User;

⚠️:这两个命令可能会导致锁定表,因此在高并发环境中使用时要谨慎,最好在低峰时段执行

对于非常大的表,执行这些命令可能需要较长时间,建议提前进行测试

三、EXPLAIN分析查询

使用 EXPLAIN 分析 SQL 执行计划可以帮助我们理解数据库如何执行查询,并找出潜在的性能瓶颈

SELECT user_id, phone, role FROM User WHERE user_id = 123 AND birthday > '2000-01-01 

先看下没有建立索引的情况

EXPLAIN SELECT user_id, phone, role FROM User WHERE user_id = 123 AND birthday > '2000-01-01 00:00:00';

 执行结果:

EXPLAIN 输出表格包含多个列,每列提供不同的查询计划信息。常见列包括:

1、id:查询的标识符,表示查询的执行顺序
2、select_type:查询类型,如 SIMPLE(简单查询),PRIMARY(主查询),UNION(联合查询的一部分),SUBQUERY(子查询)
3、table:查询涉及的表
4、type:连接类型,表示MySQL如何查找行。常见类型按效率从高到低排列为:

  • system:表只有一行(常见于系统表)
  • const:表最多有一个匹配行(索引为主键或唯一索引)
  • eq_ref:对于每个来自前一个表的行,表中最多有一个匹配行
  • ref:对于每个来自前一个表的行,表中可能有多个匹配行
  • range:使用索引查找给定范围的行
  • index:全表扫描索引
  • ALL:全表扫描

5、possible_keys:查询中可能使用的索引(user_id主键索引PRIMARY KEY
6、key:实际使用的索引。
7、key_len:使用的索引键长度
8、ref:使用的列或常量,与索引比较,此处表示常量引用,即 user_id = 123
9、rowsMySQL 预估的需要检查的行数,数值越大,查询可能越慢
10、filtered:经过表条件过滤后的行百分比
11、Extra:额外的信息

  • Using where: MySQL 在表连接后使用了过滤行
  • Using temporary: MySQL 需要使用临时表来存储结果集
  • Using filesort: MySQL 需要额外的排序操作,通常是因为没有合适的索引来排序结果

EXPLAIN 不直接告诉你一个查询是不是“慢”,但它提供了足够的信息让你评估查询的效率

如果 EXPLAIN 输出中出现 typeALLindexrows 数量很大,或者有 Using temporaryUsing filesort 出现在 Extra 列,那么这个查询很可能是慢查询

为了进一步确认,可以结合慢查询日志来获取更详细的性能数据

建立复合索引

CREATE INDEX idx_userid_birthday ON User(user_id, birthday);

执行结果 :

主键索引使用:

        默认的主键索引 user_id 被利用,使得查询可以快速定位到 user_id = 123 的记录
复合索引优化:

        创建复合索引 idx_userid_birthday 可以进一步优化查询性能,减少不必要的扫描操作

通过这些步骤,我们可以确保查询性能得到显著提升,并且索引能够充分利用

关于索引结构,可以看这篇文章:MySQL索引以及创建索引的注意事项-CSDN博客

欢迎对SQL优化的方式,进行补充……

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

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

相关文章

【51单片机】LCD1602液晶显示屏

学习使用的开发板&#xff1a;STC89C52RC/LE52RC 编程软件&#xff1a;Keil5 烧录软件&#xff1a;stc-isp 开发板实图&#xff1a; 文章目录 LCD1602存储结构时序结构 编码 —— 显示字符、数字 LCD1602 LCD1602&#xff08;Liquid Crystal Display&#xff09;液晶显示屏是…

git入门环境搭建和gui使用

git下载 git官网地址&#xff1a;https://git-scm.com/ 如果没有魔法的话&#xff0c;官网这个地址能卡死你 这里给个国内的git镜像链接 git历史版本镜像链接 然后一路next 默认路径 默认勾选就行。 今天就写到这吧&#xff0c;11点多了该睡了&#xff0c;&#xff0c;&#…

python调用MySql详细步骤

一、下载MySql MySQL :: Download MySQL Installerhttps://dev.mysql.com/downloads/windows/installer/8.0.html点击上面链接&#xff0c;进入MySQL8.0的下载页面&#xff0c;选择离线安装包下载。 不需要登陆&#xff0c;直接点击下方的 No thanks,just start my download. …

【go从零单排】通道select、通道timeout、Non-Blocking Channel Operations非阻塞通道操作

&#x1f308;Don’t worry , just coding! 内耗与overthinking只会削弱你的精力&#xff0c;虚度你的光阴&#xff0c;每天迈出一小步&#xff0c;回头时发现已经走了很远。 &#x1f4d7;概念 select 语句是 Go 的一种控制结构&#xff0c;用于等待多个通道操作。它类似于 s…

FPGA实现PCIE采集电脑端视频转SFP光口万兆UDP输出,基于XDMA+GTX架构,提供2套工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的PCIE方案10G Ethernet Subsystem实现万兆以太网物理层方案 3、PCIE基础知识扫描4、工程详细设计方案工程设计原理框图电脑端视频PCIE视频采集QT上位机XDMA配置及使用XDMA中断模块FDMA图像缓存UDP视频组包发送UDP协议栈MAC…

C++数据结构算法学习

C ,orient(面向) object , object entity(实体) Visible(可见的) or invisible(不可见) 变量用来保存数据 objects attribute(属性) services(服务) C STL 容器 vector, list&#xff08;&#xff09; vector底层是数组&#xff0c;类似双向链表和list底层 map/s…

基于Java Springboot图书馆管理系统

一、作品包含 源码数据库文档全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据库&#xff1a;MySQL8.0 数据…

三周精通FastAPI:37 包含 WSGI - Flask,Django,Pyramid 以及其它

官方文档&#xff1a;https://fastapi.tiangolo.com/zh/advanced/wsgi/ 包含 WSGI - Flask&#xff0c;Django&#xff0c;其它 您可以挂载多个 WSGI 应用&#xff0c;正如您在 Sub Applications - Mounts, Behind a Proxy 中所看到的那样。 为此, 您可以使用 WSGIMiddlewar…

gdb调试redis。sudo

1.先启动redis-server和一个redis-cli。 2.ps -aux|grep reids查看redis相关进程。 3.开始以管理员模式附加进程调试sudo gdb -p 2968.注意这里不能不加sudo&#xff0c;因为Redis 可能以 root 用户启动&#xff0c;普通用户无法附加到该进程。否则就会出现可能下列情形&#…

Python安装(ubuntu)

一&#xff1a;安装指定版本的python python3 --version直接返回ubuntu自带的3.8.10的版本 radarswradarsw-Precision-5560:~$ python3 --version Python 3.8.10通过指令直接安装&#xff0c;会报错如下; radarswradarsw-Precision-5560:~$ sudo apt install python3.11 正在…

在 Oracle Linux 8.9 上安装Oracle Database 23ai 23.5

在 Oracle Linux 8.9 上安装Oracle Database 23ai 23.5 1. 安装 Oracle Database 23ai2. 连接 Oracle Database 23c3. 重启启动后&#xff0c;手动启动数据库4. 重启启动后&#xff0c;手动启动 Listener5. 手动启动 Pluggable Database6. 自动启动 Pluggable Database7. 设置开…

在VMware虚拟机环境下识别U盘

文章目录 前言一、在自己的计算机上&#xff08;非虚拟机&#xff09;按【winR】键→输入【services.msc】→点击【确定】二、找到服务名称为【VMware USB Arbitration Service】确保为启动状态三、VMware虚拟机设置四、启动虚拟机系统&#xff0c;插入U盘查看是否能识别到U盘 …

数据库范式、MySQL 架构、算法与树的深入解析

一、数据库范式 在数据库设计中&#xff0c;范式是一系列规则&#xff0c;用于确保数据的组织和存储具有良好的结构、完整性以及最小化的数据冗余。如果不遵循范式设计&#xff0c;数据可能会以平铺式罗列&#xff0c;仅使用冒号、分号等简单分隔。这种方式存在诸多弊端&#…

Leetcode 存在重复元素II

这段代码的算法思想可以用以下步骤来解释&#xff1a; 算法思想 使用哈希表&#xff08;HashMap&#xff09;存储每个元素的索引&#xff1a; 遍历数组 nums 时&#xff0c;使用一个 HashMap 来记录每个元素的值和它的索引位置。这样可以快速查找之前出现过的相同元素的索引。…

Vue3.js - 一文看懂Vuex

1. 前言 Vuex 是 Vue.js 的官方状态管理库&#xff0c;用于在 Vue 应用中管理组件之间共享的状态。Vuex 适用于中大型应用&#xff0c;它将组件的共享状态集中管理&#xff0c;可以避免组件间传递 props 或事件的复杂性。 2. 核心概念 我们可以将Vuex想象为一个大型的Vue&…

面试编程题目(一)细菌总数计算

题目如图&#xff1a; 第一题&#xff1a; import lombok.AllArgsConstructor; import lombok.Data;import java.util.Arrays; import java.util.Collections; import java.util.List;/*** description: 细菌实体类* author: zhangmy* Version: 1.0* create: 2021-03-30 11:2…

基于Qt/C++与OpenCV库 实现基于海康相机的图像采集和显示系统(工程源码可联系博主索要)

本文将梳理一个基于 Qt 和 OpenCV 实现的海康相机图像采集 Demo。该程序能够实现相机连接、参数设置、图像采集与显示、异常处理等功能&#xff0c;并通过 Qt 界面展示操作结果。 1. 功能概述 该程序使用 Qt 的 GUI 作为界面&#xff0c;OpenCV 作为图像处理库&#xff0c;通…

网络基础Linux

目录 计算机网络背景 网络发展 认识 "协议" 网络协议初识 OSI七层模型 TCP/IP五层(或四层)模型 网络传输基本流程 网络传输流程图 ​编辑 数据包封装和分用 网络中的地址管理 认识IP地址 认识MAC地址 笔记&#xff08;画的图&#xff09; 协议&#x…

大数据新视界 -- 大数据大厂之 Impala 存储格式转换:从原理到实践,开启大数据性能优化星际之旅(下)(20/30)

&#x1f496;&#x1f496;&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎你们来到 青云交的博客&#xff01;能与你们在此邂逅&#xff0c;我满心欢喜&#xff0c;深感无比荣幸。在这个瞬息万变的时代&#xff0c;我们每个人都在苦苦追寻一处能让心灵安然栖息的港湾。而 我的…

性能调优专题(12)之垃圾收集器ParNewCMS与底层三色标记算法详解

一、垃圾收集算法 1.1 分代收集理论 当前虚拟机的垃圾收集器都采用分代收集理论&#xff0c;只是根据对象存活周期的不同将内存分为几块。一般Java将堆分为新生代和老年代&#xff0c;这样子我们就可以根据各个年代的特点选择合适的垃圾收集算法。 比如在新生代中&#xff0c;每…