MySQL SQL语句性能优化

MySQL SQL语句性能优化指南

  • 一、查询设计优化
    • 1. 避免 SELECT *
    • 2. 使用 WHERE 进行条件过滤
    • 3. 避免在索引列上使用函数和表达式
    • 4. 使用 LIMIT 限制返回行数
    • 5. 避免使用子查询
    • 6. 优化 JOIN 操作
    • 7. 避免全表扫描
  • 二、索引优化
    • 1. 使用合适的索引
    • 2. 覆盖索引
    • 3. 索引选择性
    • 4. 多列索引顺序
  • 三、表结构优化
    • 1. 垂直拆分
    • 2. 水平分区
    • 3. 使用适当的数据类型
  • 四、查询缓存优化
    • 1. 查询缓存的工作原理
    • 2. 配置查询缓存
    • 3. 查询缓存的优缺点
    • 4. 查询缓存的最佳实践
  • 五、配置优化
    • 1. 调整连接池大小
    • 2. 使用慢查询日志
  • 六、其他优化技巧
    • 1. 避免使用临时表
    • 2. 使用批量插入
    • 3. 定期优化表
    • 4. 避免使用锁表
  • 七、使用 EXPLAIN 分析查询
  • 总结


MySQL作为一款流行的关系型数据库管理系统,广泛应用于各类应用系统中。然而,随着数据量的增加和查询复杂度的提高,SQL查询性能可能会成为系统瓶颈。本文将系统地介绍MySQL SQL语句性能优化的原则和方法,帮助提升数据库的运行效率。

一、查询设计优化

1. 避免 SELECT *

SELECT * 会检索表中的所有列,可能会带来不必要的I/O开销和网络传输。因此,应尽量选择需要的列。

-- 不推荐
SELECT * 
FROM users 
WHERE id = 1;-- 推荐
SELECT id, username, email 
FROM users 
WHERE id = 1;

2. 使用 WHERE 进行条件过滤

在查询中尽量使用 WHERE 子句进行条件过滤,减少全表扫描的行数,从而提高查询效率。

-- 不推荐
SELECT * 
FROM orders;-- 推荐
SELECT * 
FROM orders 
WHERE status = 'completed';

3. 避免在索引列上使用函数和表达式

WHERE 子句中的索引列上使用函数或表达式会导致无法使用索引,影响查询性能。

-- 不推荐
SELECT * 
FROM users 
WHERE YEAR(created_at) = 2024;-- 推荐
SELECT * 
FROM users 
WHERE created_at BETWEEN '2024-12-01' AND '2024-12-10';

4. 使用 LIMIT 限制返回行数

对于需要分页显示的数据,应使用 LIMIT 限制返回的行数,避免一次性读取过多数据。

SELECT * 
FROM orders 
WHERE status = 'completed' 
LIMIT 100;

5. 避免使用子查询

在可能的情况下,尽量避免使用子查询,而是使用连接(JOIN)来优化查询。

-- 不推荐
SELECT * 
FROM users
WHERE id IN (SELECT user_id FROM orders WHERE status = 'completed');-- 推荐
SELECT users.* 
FROM users 
JOIN orders ON users.id = orders.user_id 
WHERE orders.status = 'completed';

6. 优化 JOIN 操作

在使用JOIN操作时,确保被连接的列上有索引,并尽量减少JOIN的数量和复杂度。

-- 创建索引
CREATE INDEX idx_orders_user_id ON orders(user_id);-- 使用索引优化JOIN查询
SELECT users.* 
FROM users 
JOIN orders ON users.id = orders.user_id 
WHERE orders.status = 'completed';

7. 避免全表扫描

当表中的数据量非常大时,执行没有过滤条件的查询或者查询条件不适合索引时,数据库可能需要进行全表扫描。

这不仅会增加查询时间,还会加重数据库负担。为了避免全表扫描,应该尽量通过索引列、合理的过滤条件等优化查询,减少扫描的数据量。

-- 不推荐
SELECT * FROM users WHERE name LIKE '%J%';-- 推荐
SELECT * FROM users WHERE user_id = 123 AND name LIKE '%J%';

在上述查询中,name LIKE '%J%' 会导致全表扫描,因为数据库无法利用索引来加速这种模糊匹配操作,特别是当表中的数据量非常大的时候,查询会非常慢。

改进后的查询通过添加具有索引user_id 作为条件,能够利用索引优化查询,避免全表扫描。

二、索引优化

1. 使用合适的索引

为常用的查询条件和排序条件添加索引,避免全表扫描。

-- 创建索引
CREATE INDEX idx_users_username ON users(username);-- 使用索引的查询
SELECT * 
FROM users 
WHERE username = 'john_doe';

2. 覆盖索引

覆盖索引包含查询所需的所有列,可以避免回表查询,进一步提高查询性能。

-- 创建覆盖索引
CREATE INDEX idx_orders_status_created_at ON orders(status, created_at);-- 使用覆盖索引的查询
SELECT status, created_at 
FROM orders 
WHERE status = 'completed';

3. 索引选择性

索引的选择性(即唯一值的比例)越高,索引的效率越高。对于低选择性的列(如性别),单独建立索引效果不佳,应考虑与其他高选择性列组合建立联合索引。

4. 多列索引顺序

在创建多列索引时,应将选择性高的列放在索引的前面,以提高索引的效率。

-- 选择性高的列在前
CREATE INDEX idx_users_lastname_firstname ON users(lastname, firstname);-- 查询时利用多列索引
SELECT * 
FROM users 
WHERE lastname = 'Smith' AND firstname = 'John';

三、表结构优化

1. 垂直拆分

将表中使用频率不同的字段拆分到不同的表中,减少查询的复杂度和数据量。

-- 原始表
CREATE TABLE user_details (id INT PRIMARY KEY,username VARCHAR(50),email VARCHAR(100),address TEXT,phone_number VARCHAR(20)
);-- 拆分后的表
CREATE TABLE users (id INT PRIMARY KEY,username VARCHAR(50),email VARCHAR(100)
);CREATE TABLE user_contacts (user_id INT,address TEXT,phone_number VARCHAR(20),FOREIGN KEY (user_id) REFERENCES users(id)
);

2. 水平分区

对于数据量非常大的表,可以使用分区来提高查询性能。

-- 创建分区表
CREATE TABLE orders (id INT,order_date DATE,amount DECIMAL(10, 2)
) PARTITION BY RANGE (YEAR(order_date)) (PARTITION p2020 VALUES LESS THAN (2021),PARTITION p2021 VALUES LESS THAN (2022)
);

3. 使用适当的数据类型

选择适当的数据类型可以减少存储空间和提高查询性能。例如,使用整数类型代替字符串类型作为主键。

-- 使用整数类型作为主键
CREATE TABLE users (id INT PRIMARY KEY,username VARCHAR(50),email VARCHAR(100)
);

四、查询缓存优化

在 MySQL 中,查询缓存是一个用于存储 SELECT 查询结果的机制。通过查询缓存,MySQL 可以避免重复执行相同的查询,直接从缓存中返回结果,从而显著提高查询性能,减少数据库负载。

1. 查询缓存的工作原理

查询缓存将查询的结果存储在内存中,并且是基于查询的文本来缓存的。只要查询的 SQL 语句完全相同,MySQL 会直接从缓存中获取结果,而不是重新执行查询。

工作流程:

  1. 用户提交查询时,MySQL 会首先检查查询缓存中是否存在相同的查询结果。
  2. 如果缓存中存在查询结果,MySQL 会直接返回缓存中的结果。
  3. 如果缓存中不存在结果,MySQL 会执行查询,将结果存入缓存,并返回给用户。

注意:查询缓存只会缓存 SELECT 查询的结果,不会缓存 INSERT、UPDATE、DELETE 等修改数据的操作。

2. 配置查询缓存

启用查询缓存

在 MySQL 配置文件 my.cnf 中,可以通过设置以下选项来启用查询缓存:
适当调整MySQL的缓存参数,如 query_cache_sizeinnodb_buffer_pool_size 等,可以提高查询性能。

[mysqld]
query_cache_type = 1                # 启用查询缓存
query_cache_size = 256M             # 设置查询缓存大小
query_cache_limit = 1M              # 设置缓存的查询大小限制,超过此大小的查询将不缓存
  • query_cache_type:指定查询缓存的启用方式。1 表示启用查询缓存,0 表示禁用查询缓存,2 表示只有 SQL_NO_CACHE(禁用缓存)标记的查询才不缓存。
  • query_cache_size:设置查询缓存的大小,单位为字节。合理设置缓存大小可以避免过多的内存消耗。
  • query_cache_limit:设置缓存的查询结果大小限制。如果查询的结果超过该大小,则不缓存。

动态调整查询缓存(运行时)

除了在配置文件中设置外,也可以通过 SQL 命令在运行时动态调整查询缓存的大小和启用状态:

-- 启用查询缓存
SET global query_cache_size = 1000000;   # 设置查询缓存大小为 1MB
SET global query_cache_type = 1;          # 启用查询缓存-- 执行查询
SELECT * FROM users WHERE username = 'John';

SET global query_cache_size:此命令设置查询缓存的大小。在此示例中,将缓存大小设置为 1MB。
SET global query_cache_type:设置查询缓存的启用类型。1 表示启用查询缓存。

查看查询缓存的状态
你可以通过以下 SQL 命令查看查询缓存的状态:

SHOW VARIABLES LIKE 'query_cache%';
SHOW STATUS LIKE 'Qcache%';

这些命令会显示与查询缓存相关的配置信息和当前状态:

  • Qcache_free_blocks:查询缓存中空闲的块数。
  • Qcache_hits:查询缓存命中次数。
  • Qcache_inserts:查询缓存插入次数。
  • Qcache_lowmem_prunes:查询缓存由于内存不足而被清理的次数。
  • Qcache_not_cached:未缓存的查询次数。

3. 查询缓存的优缺点

优势

  • 减少数据库负载:查询缓存通过缓存 SELECT 查询的结果,避免了对数据库的重复访问,尤其是在读取密集型应用中。

  • 提高响应速度:查询缓存使得相同查询不再执行,而是直接返回缓存结果,减少查询时间,提升应用性能。

劣势

  • 缓存失效:当表中的数据发生变化(如 INSERT、UPDATE、DELETE 操作)时,查询缓存会失效。这意味着缓存可能会在某些操作后被清空或无效,导致重新计算查询结果。

  • 占用内存:查询缓存会占用一定的内存空间,特别是在缓存较大的查询结果时。如果配置不当,可能会导致内存压力过大。

  • 适用场景限制:查询缓存对于频繁变更的数据表效果较差,因为每次数据更新都会导致缓存失效。在高并发的环境中,查询缓存可能会造成性能瓶颈。

  • 全表扫描问题:对于需要扫描大量数据的查询,查询缓存并不能显著提高性能。

4. 查询缓存的最佳实践

适用于读取密集型的应用

查询缓存对于那些以读取操作为主且数据变化不频繁的应用非常有效。在这种场景下,缓存的查询结果可以显著提高应用性能,减少对数据库的请求。

  • 数据分析报表:如果一个报表的查询结果不经常改变,查询缓存可以有效提高查询速度。

  • 商品信息查询:电商网站中,商品信息的变化不频繁,查询缓存可以用来缓存商品查询结果,提升响应速度。

不适用于频繁更新的数据表

查询缓存不适用于频繁更新的表,特别是数据表中频繁的 INSERT、UPDATE 或 DELETE 操作会导致查询缓存的频繁失效,降低性能。

  • 电商订单表:订单数据频繁变化,查询缓存的使用可能会导致性能瓶颈,因为每次更新都会清除缓存。

  • 社交平台的用户动态:频繁的动态数据更新使得查询缓存无法有效提升性能,甚至可能会造成缓存失效和资源浪费。

五、配置优化

1. 调整连接池大小

根据应用的并发需求调整数据库连接池的大小,避免连接不足或过多。

-- 连接池配置示例(在 my.cnf 文件中)
[mysqld]
max_connections = 5000

2. 使用慢查询日志

启用慢查询日志,找出执行时间长的查询,进行针对性优化。

-- 启用慢查询日志(在 my.cnf 文件中)
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2

六、其他优化技巧

1. 避免使用临时表

临时表会增加I/O操作,应尽量避免使用。如果必须使用,确保临时表有适当的索引。

2. 使用批量插入

对于大量数据插入操作,使用批量插入可以显著提高效率,减少数据库连接次数和事务开销。

-- 批量插入示例
INSERT INTO users (username, email) 
VALUES ('user1', 'user1@example.com'), ('user2', 'user2@example.com');

3. 定期优化表

定期使用 OPTIMIZE TABLE 命令优化表结构,减少碎片,提高查询性能。

-- 优化表
OPTIMIZE TABLE users;

4. 避免使用锁表

尽量避免使用 LOCK TABLES,以减少锁争用,提升并发性能。

七、使用 EXPLAIN 分析查询

使用 EXPLAIN 语句分析查询执行计划,找出查询中的瓶颈和潜在的优化点。

EXPLAIN 
SELECT * 
FROM orders 
WHERE status = 'completed' AND order_date BETWEEN '2020-01-01' AND '2020-12-31';

通过 EXPLAIN 的输出,可以了解查询是如何执行的,包括使用了哪些索引,扫描了多少行等。根据这些信息,可以进一步优化查询。


总结

  1. 查询设计:减少数据量,避免复杂计算和函数操作。
  2. 索引使用:合理创建索引,利用覆盖索引。
  3. 表结构:垂直拆分和水平分区,选择合适的数据类型。
  4. 配置优化:调整缓存和连接池,启用慢查询日志。
  5. 其他技巧:避免临时表和锁表,使用批量插入和定期优化表。
  6. 分析工具:使用 EXPLAIN 分析查询执行计划。

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

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

相关文章

Mybatis动态sql执行过程

动态SQL的执行原理主要涉及到在运行时根据条件动态地生成SQL语句,然后将其发送给数据库执行。以下是动态SQL执行原理的详细解释: 一、接收参数 动态SQL首先会根据用户的输入或系统的条件接收参数。这些参数可以是查询条件、更新数据等,它们…

java jar包加密 jar-protect

介绍 java 本身是开放性极强的语言,代码也容易被反编译,没有语言层面的一些常规保护机制,jar包很容易被反编译和破解。 受classfinal(已停止维护)设计启发,针对springboot日常项目开发,重新编写安全可靠的jar包加壳加密技术,用于保护软件版权。 使用说…

Linux:Git

Git常见指令: git help xx_command git xx_command --help git --version 查看git版本git config --global user.name "xxx_name" 全局级别的签名设置,全局的放在本用 git config --global user.ema…

【WiFi】WiFi中RSSI、SNR、NF之间关系及说明

RSSI(接收信号强度指示) 定义: RSSI 是一个相对值,用于表示接收到的无线信号的强度。它通常由无线设备的硬件(如无线网卡或无线芯片)直接提供。 计算: RSSI 的计算通常是由设备的无线芯片完成的…

提升音频转录准确性:VAD技术的应用与挑战

引言 在音频转录技术飞速发展的今天,我们面临着一个普遍问题:在嘈杂环境中,转录系统常常将非人声误识别为人声,导致转录结果出现错误。例如,在whisper模式下,系统可能会错误地转录出“谢谢大家”。本文将探…

[ZMQ] -- ZMQ通信Protobuf数据结构 1

1、前言背景 工作需要域间实现zmq通信,刚开始需要比较简单的数据结构,比如两个bool,后面可能就需要传输比较大的数据,所以记录下实现流程,至于为啥选择proto数据结构去做大数据传输,可能是地平线也用这个&…

顺序表的使用,对数据的增删改查

主函数: 3.c #include "3.h"//头文件调用 SqlListptr sql_cerate()//创建顺序表函数 {SqlListptr ptr(SqlListptr)malloc(sizeof(SqlList));//在堆区申请连续的空间if(NULLptr){printf("创建失败\n");return NULL;//如果没有申请成功&#xff…

React和Vue中暴露子组件的属性和方法给父组件用,并且控制子组件暴露的颗粒度的做法

React 在 React 中,forwardRef 是一种高级技术,它允许你将 ref 从父组件传递到子组件,从而直接访问子组件的 DOM 节点或公开的方法。这对于需要操作子组件内部状态或 DOM 的场景非常有用。为了使子组件能够暴露其属性和方法给父组件&#xf…

《C++ 实时视频流物体跟踪与行为分析全解析》

在当今科技飞速发展的时代,视频监控与智能分析技术在众多领域发挥着极为重要的作用。从安防监控到智能交通,从工业自动化到人机交互,利用 C 处理实时视频流中的物体跟踪和行为分析成为了热门且极具挑战性的研究与开发方向。本文将深入探讨其中…

5G中的随机接入过程可以不用收RAR?

有朋友提到了一种不用接收RAR的RA过程,问这个是怎么回事。其实在刚刚写过的LTM cell switch篇章中就有提到,这里把所有相关的内容整理如下。 在RACH-less LTM场景,在进行LTM cell switch之前就要先知道target cell的TA信息,进而才…

git 导出某段时间修改的文件 windows

第一步:列出两次commitID之间的文件变动 git diff oldid newid --name-only// 例如 git diff 4a886c57a8b5611a2abcfcd120461c2e92f7029a HEAD --name-only 4a886c57a8b5611a2abcfcd120461c2e92f7029a 代表之前 HEAD 代表最新或者换成某次commitID 例如&#xf…

Qt 联合Halcon配置

文章目录 配置代码窗口绑定 配置 选择添加库 选择外部库 LIBS -LC:/Program Files/MVTec/HALCON-17.12-Progress/lib/x64-win64/ LIBS -lhalconcpp\-lhdevenginecpp\-lhalconINCLUDEPATH C:/Program Files/MVTec/HALCON-17.12-Progress/include DEPENDPATH C:/Program Fil…

new URL(`../assets/images/${name}`, import.meta.url).href

背景: 文章讲述了Vite框架中关于资源文件(如图片)在默认配置下,如何正确处理开发环境和打包后的不同引用方式。重点介绍了使用import.meta.url和new URL() 来动态获取并处理静态资源URL的方法,以及注意事项&#xff0…

8、笔记本品牌分类介绍:LG - 计算机硬件品牌系列文章

LG笔记本品牌以其高性能和先进技术而闻名,‌提供多种型号以满足不同用户的需求。‌ LG笔记本产品线包括多种类型,‌以满足不同用户的需求。‌其中,‌LG Gram Pro系列以其超薄设计和高性能配置受到关注。‌该系列笔记本采用16:10的OLED显示屏&…

367_C++_计算mouse移动过程中,视频框的右侧、底部边距,以及根据实时的右侧、底部边距计算—视频框的左上角位置

代码分析 1. restorePos 方法 restorePos 的作用是恢复 NavigationFrame 的位置,将其移动到父窗口或者指定矩形内的特定位置。 void NavigationFrame::restorePos() {// 获取目标矩形:优先使用 `m_pRect`,否则默认使用视频区域或父窗口区域RSRect videoRect(m_pVide

Tiptap,: 富文本编辑器入门与案例分析

Tiptap 是一个现代的富文本编辑器,基于 ProseMirror 打造,旨在提供一个灵活且功能强大的文本编辑解决方案。它具有开箱即用的能力,同时也允许开发者根据业务需求进行高度定制化扩展。与传统的富文本编辑器相比,Tiptap 提供了更精细…

scala的泛型类

泛型:类型参数化 泛型类指的是把泛型定义到类的声明上, 即:该类中的成员的参数类型是由泛型来决定的. 在创建对象时, 明确具体的数据类型. 定义格式: class 类名(成员名:数据类型) class 类名[泛型名](成员名:泛型名) 参考代…

对比损失(Contrastive Loss)与大模型:Contrastive Loss and Large Models (中英双语)

对比损失(Contrastive Loss)与大模型:从原理到实践 在现代深度学习中,对比损失(Contrastive Loss)是一种核心技术,尤其是在对比学习(Contrastive Learning)中被广泛使用…

Java基础学习:java常用启动命令

一、java -jar 1、系统属性传递 使用形式:java -DpathD:\jacoco -jar 获取方式:System.getProperties() 2、系统参数传递 使用形式:java -jar application.jar --jacocoPathD:\tomcat 获取方式:通过启动方法入口main的参数arg…

Linux下SVN客户端保存账号密码

参考文章:解决:Linux上SVN 1.12版本以上无法直接存储明文密码_linux svn 保存密码-CSDN博客新版本svn使用gpg-agent存储密码-CSDN博客svn之无法让 SVN 存储密码,即使配置设置为允许_编程设计_ITGUEST 方法一:明文方式保存密码 首…