MySQL 优化方案
一、MySQL 查询过程
MySQL 查询过程是指从客户端发送 SQL 语句到 MySQL 服务器,再到服务器返回结果集的整个过程。这个过程涉及多个组件的协作,包括连接管理、查询解析、优化、执行和结果返回等。

1.1 查询过程的关键组件
- 连接管理器:管理客户端连接。
- 解析器:解析 SQL 语句。
- 优化器:生成执行计划。
- 执行引擎:执行查询。
- 存储引擎:存储和检索数据。
1.2 查询过程的详细步骤
-
客户端发送查询请求
- 客户端(如应用程序、命令行工具)通过 MySQL 协议(如 TCP/IP)向 MySQL 服务器发送 SQL 查询请求。
- 请求内容可以是 SELECT、INSERT、UPDATE、DELETE 等 SQL 语句。
-
连接管理
- MySQL 服务器接收到请求后,首先由 连接管理器 处理。
- 连接管理器负责:
- 验证客户端的用户名和密码。
- 检查客户端的权限(是否有权限执行该查询)。
- 分配一个线程来处理该请求(MySQL 是多线程架构,每个连接由一个线程处理)。
-
查询缓存(MySQL 8.0 之前)
- 在 MySQL 8.0 之前,服务器会检查查询缓存(Query Cache)。
- 如果查询缓存中已经存在该查询的结果,则直接返回缓存结果。
- 如果查询缓存未命中,则继续后续步骤。
- 在MySQL 8.0 移除了查询缓存功能,因为在高并发场景下,查询缓存可能成为性能瓶颈。
- 在 MySQL 8.0 之前,服务器会检查查询缓存(Query Cache)。
-
查询解析
- 解析器(Parser) 对 SQL 语句进行词法分析和语法分析。
- 词法分析:将 SQL 语句拆分为关键字、表名、列名、操作符等 token。
- 语法分析:检查 SQL 语句是否符合 MySQL 的语法规则。
- 如果 SQL 语句有语法错误,解析器会返回错误信息。
- 解析器(Parser) 对 SQL 语句进行词法分析和语法分析。
-
查询优化
- 查询优化器(Optimizer) 对 SQL 语句进行优化,生成一个高效的执行计划。
- 优化器会考虑以下因素:
- 使用哪些索引。
- 表的连接顺序(JOIN 的顺序)。
- 是否可以使用覆盖索引。
- 是否可以使用索引合并(Index Merge)。
- 优化器会生成多个可能的执行计划,并选择成本最低的一个。
- 优化器会考虑以下因素:
- 可以通过 EXPLAIN 命令查看优化器生成的执行计划。
- 查询优化器(Optimizer) 对 SQL 语句进行优化,生成一个高效的执行计划。
-
查询执行
- 执行引擎(Execution Engine) 根据优化器生成的执行计划,调用存储引擎的接口执行查询。
- 执行引擎负责:
- 打开表。
- 读取数据(通过索引或全表扫描)。
- 执行排序、分组、聚合等操作。
- 处理 JOIN 操作。
- 执行引擎与存储引擎(如 InnoDB、MyISAM)交互,获取数据。
- 执行引擎负责:
- 执行引擎(Execution Engine) 根据优化器生成的执行计划,调用存储引擎的接口执行查询。
-
存储引擎处理
- 存储引擎(Storage Engine) 负责数据的存储和检索。
- 存储引擎根据执行引擎的请求,从磁盘或内存中读取数据。
- 存储引擎会将数据返回给执行引擎。
- 存储引擎(Storage Engine) 负责数据的存储和检索。
-
结果返回
- 执行引擎将处理后的数据返回给客户端。
- 如果查询涉及排序、分组或聚合,执行引擎会在返回结果前完成这些操作。
- 结果集通过 MySQL 协议发送给客户端。
- 客户端接收到结果后,可以继续处理数据(如显示、存储或进一步计算)。
- 执行引擎将处理后的数据返回给客户端。
-
日志记录
- MySQL 会根据配置记录相关日志:
- 二进制日志(Binlog):记录所有修改数据的操作(如 INSERT、UPDATE、DELETE),用于主从复制和数据恢复。
- 慢查询日志(Slow Query Log):记录执行时间超过阈值的查询,用于性能分析。
- 通用日志(General Log):记录所有查询请求,用于调试。
- MySQL 会根据配置记录相关日志:
-
连接关闭
- 查询完成后,客户端可以选择关闭连接或继续发送新的查询请求。
- 如果连接空闲时间超过 wait_timeout,MySQL 会自动关闭连接以释放资源。
二、SQL 优化方案
2.1 索引优化
索引是提高查询性能的核心手段,但需要合理使用。
- 创建合适的索引
- 单列索引:对经常用于 WHERE、JOIN、ORDER BY 和 GROUP BY 的列创建索引。
CREATE INDEX idx_name ON table_name(column_name); - 复合索引:对多个列的组合查询创建复合索引。
CREATE INDEX idx_name ON table_name(column1, column2); - 前缀索引:对文本列的前缀创建索引,减少索引大小。
CREATE INDEX idx_name ON table_name(column_name(10));
- 单列索引:对经常用于 WHERE、JOIN、ORDER BY 和 GROUP BY 的列创建索引。
- 避免过度索引
- 索引会增加写操作(INSERT、UPDATE、DELETE)的开销,因此不要为不常用的列创建索引。
- 删除未使用或重复的索引。
DROP INDEX idx_name ON table_name;
- 使用覆盖索引
- 如果查询只需要从索引中获取数据,而不需要回表查询数据行,可以显著提升性能。
SELECT column1, column2 FROM table_name WHERE column1 = 'value'; -- 确保 (column1, column2) 上有索引
- 如果查询只需要从索引中获取数据,而不需要回表查询数据行,可以显著提升性能。
- 避免索引失效
- 避免在索引列上使用函数或表达式
- 示例:
-- 索引失效 SELECT * FROM users WHERE YEAR(created_at) = 2023; - 原因:MySQL 无法对 YEAR(created_at) 使用索引,因为它需要对每一行的 created_at 进行计算。
- 优化方法:
-- 优化后 SELECT * FROM users WHERE created_at >= '2023-01-01' AND created_at < '2024-01-01';
- 示例:
- 避免在索引列上使用 NOT、!= 或 <>
- 示例:
-- 索引失效 SELECT * FROM users WHERE status != 'active'; - 原因:NOT、!= 或 <> 需要扫描所有不等于条件的值,无法有效利用索引。
- 优化方法:尽量避免使用 !=,尝试改写查询逻辑。
- 示例:
- 避免在索引列上使用 OR 条件
- 示例:
-- 索引失效 SELECT * FROM users WHERE age = 25 OR name = 'John'; - 原因:如果 name 列没有索引,MySQL 无法使用 age 列的索引。
- 优化方法:
-- 优化后 SELECT * FROM users WHERE age = 25 UNION SELECT * FROM users WHERE name = 'John';
- 示例:
- 避免在索引列上使用 LIKE 以通配符开头
- 示例:
-- 索引失效 SELECT * FROM users WHERE name LIKE '%John%'; - 原因:当通配符%出现在查询字符串的开头时,MySQL无法利用索引的前缀部分来加速查询。
- 优化方法:
- 尽量避免以 % 开头的模糊查询。
- 如果必须使用,考虑全文索引(FULLTEXT)或其他搜索引擎(如 Elasticsearch)。
- 示例:
- 避免数据类型不匹配
- 示例:
-- 索引失效 SELECT * FROM users WHERE phone = 123456789; - 原因:如果 phone 列是字符串类型,而查询条件是数字类型,MySQL 会对索引字段进行隐式类型转换,导致索引失效。
- 优化方法:
-- 优化后 SELECT * FROM users WHERE phone = '123456789';
- 示例:
- 避免复合索引未遵循最左前缀原则
- 示例:
-- 创建复合索引 CREATE INDEX idx_name_age ON users(name, age);-- 索引失效 SELECT * FROM users WHERE age = 25; - 原因:没有遵循最左前缀原则,MySQL 无法利用复合索引的有序性,从而导致索引失效。
- 优化方法:确保查询条件包含复合索引的最左列。
-- 优化后 SELECT * FROM users WHERE name = 'John' AND age = 25;
- 示例:
- 避免在低区分度的字段上建索引
- 示例:
-- 索引失效 SELECT * FROM users WHERE sex = '男'; - 原因:在低区分度的字段上,索引树中每个键值对应的数据行数非常多。查询时,MySQL 需要扫描大量数据行,索引的效果几乎等同于全表扫描。
- 优化方法:尽量避免对低选择性的列创建索引。
- 示例:
- 避免在索引列上使用函数或表达式
2.2 查询重构
优化查询语句本身可以显著提升性能。
-
避免 SELECT *
- 只选择需要的列,减少数据传输和内存占用。
-- 不推荐 SELECT * FROM table_name; -- 推荐 SELECT column1, column2 FROM table_name;
- 只选择需要的列,减少数据传输和内存占用。
-
使用 LIMIT 时避免高偏移量
- 当 OFFSET 值很大时,MySQL 需要扫描大量数据才能找到起始位置,导致性能下降。
-- 不推荐 SELECT * FROM table_name LIMIT 10 OFFSET 100000; -- 推荐 SELECT * FROM table_name WHERE id > 100000 ORDER BY id LIMIT 10;
- 当 OFFSET 值很大时,MySQL 需要扫描大量数据才能找到起始位置,导致性能下降。
-
避免子查询
- 子查询的内部执行计划是先执行子查询再执行外层查询,由于每次执行子查询都需要创建并删除临时表,会消耗大量资源,从而影响数据库性能。
-- 不推荐 SELECT * FROM table_name WHERE column1 IN (SELECT column1 FROM table2); -- 推荐 SELECT t1.* FROM table_name t1 JOIN table2 t2 ON t1.column1 = t2.column1;
- 子查询的内部执行计划是先执行子查询再执行外层查询,由于每次执行子查询都需要创建并删除临时表,会消耗大量资源,从而影响数据库性能。
-
JOIN 查询优化
- 确保 JOIN 列上有索引:JOIN 条件中的列(通常是外键列)必须有索引。
- 小表驱动大表:MySQL 通常会选择较小的表作为驱动表,以减少扫描的行数。
- 过滤数据:在 JOIN 之前,使用 WHERE 条件减少参与 JOIN 的数据量。
SELECT * FROM table1 t1 JOIN table2 t2 ON t1.id = t2.id WHERE t1.column = 'value';
-
合理使用 IN 和 EXISTS
- IN
- 用法:用于判断某个值是否在子查询返回的结果集中。
- 适用场景:当子查询返回的结果集较小时,IN 的性能较好。
- 执行过程:
- 执行子查询,获取结果集。
- 将结果集加载到内存中。
- 对外部查询的每一行,检查是否在结果集中。
- EXISTS
- 用法:用于判断子查询是否返回任何行。如果子查询返回至少一行,EXISTS 返回 TRUE,否则返回 FALSE。
- 适用场景:当子查询返回的结果集较大时,EXISTS 的性能较好。
- 执行过程:
- 对外部查询的每一行,执行子查询。
- 如果子查询返回至少一行,则返回 TRUE。
- IN
-
使用 EXPLAIN 分析查询
- 使用 EXPLAIN 查看查询执行计划,找出性能瓶颈。
- 关注 type(访问类型)、key(使用的索引)、rows(扫描的行数)等字段。
EXPLAIN SELECT * FROM table_name WHERE column1 = 'value';
2.3 表结构优化
- 选择合适的数据类型
- 使用最小的数据类型存储数据,例如:
- 使用 INT 而不是 BIGINT。
- 使用 VARCHAR 而不是 TEXT。
- 避免使用 NULL,尽量使用默认值。
- 使用最小的数据类型存储数据,例如:
- 规范化与反规范化
- 规范化:减少数据冗余,提高数据一致性。
- 反规范化:在查询频繁的场景下,适当冗余数据以减少 JOIN 操作。
- 分区表
- 对大表进行分区,提升查询性能。
CREATE TABLE table_name (id INT,created_at DATE ) PARTITION BY RANGE (YEAR(created_at)) (PARTITION p0 VALUES LESS THAN (2020),PARTITION p1 VALUES LESS THAN (2021),PARTITION p2 VALUES LESS THAN (2022) );
- 对大表进行分区,提升查询性能。
2.4 配置优化
- 调整缓冲区大小
- 增加 innodb_buffer_pool_size,使其足够容纳常用数据。
SET GLOBAL innodb_buffer_pool_size = 1G;
- 增加 innodb_buffer_pool_size,使其足够容纳常用数据。
- 调整查询缓存
- 在 MySQL 8.0 之前,可以启用查询缓存(适用于读多写少的场景)。
SET GLOBAL query_cache_size = 64M;
- 在 MySQL 8.0 之前,可以启用查询缓存(适用于读多写少的场景)。
- 调整连接数
- 增加最大连接数,避免连接耗尽。
SET GLOBAL max_connections = 500;
- 增加最大连接数,避免连接耗尽。
- 调整日志配置
- 关闭不必要的日志(如慢查询日志、二进制日志)以减少 I/O 开销。
SET GLOBAL slow_query_log = OFF;
- 关闭不必要的日志(如慢查询日志、二进制日志)以减少 I/O 开销。
2.5 其他优化技巧
- 批量操作
- 使用批量插入或更新,减少单条语句的开销。
INSERT INTO table_name (column1, column2) VALUES ('value1', 'value2'), ('value3', 'value4');
- 使用批量插入或更新,减少单条语句的开销。
- 使用缓存
- 使用 Redis 或 Memcached 缓存热点数据,减少数据库压力。
- 分库分表
- 对于超大规模数据,考虑分库分表(如使用 ShardingSphere 或 MyCat)。
相关文章:
MySQL 优化方案
一、MySQL 查询过程 MySQL 查询过程是指从客户端发送 SQL 语句到 MySQL 服务器,再到服务器返回结果集的整个过程。这个过程涉及多个组件的协作,包括连接管理、查询解析、优化、执行和结果返回等。 1.1 查询过程的关键组件 连接管理器:管理…...
智能对话小程序功能优化day1-登录鉴权
目录 1.数据库表构建。 2.完善登录相关的实例对象。 3.登录相关功能实现。 4.小程序效果。 最近尝试下trae加入claude3.7后的读图生成代码功能,可以看到简单的页面一次性生成确实准确率高了不少,想起来之前笔记中开发的智能问答小程序功能还是有些简…...
【架构艺术】Go语言微服务monorepo的代码架构设计
近期因为项目架构升级原因,笔者着手调研一些go项目monorepo的代码架构设计,目标是长期把既有微服务项目重要的部分都转移到monorepo上面,让代码更容易维护,协作开发更加方便。虽然经验不多,但既然有了初步的调研&#…...
MinIO的预签名直传机制
我们传统使用MinIo做OSS对象存储的应用方式往往都是在后端配置与MinIO的连接和文件上传下载的相关接口,然后我们在前端调用这些接口完成文件的上传下载机制,但是,当并发量过大,频繁访问会对后端的并发往往会对服务器造成极大的压力…...
谈谈List,Set,Map的区别
List、Set 和 Map 是 Java 集合框架(Java Collections Framework)中的三种主要接口,它们各自有不同的特点和用途。以下是它们的区别和使用场景的详细解释: 1. List(列表) 1.1 特点 有序集合:Li…...
投资晚报 3.12
一、 晚间要闻 1、CME美联储观察:美联储3月降息25个基点的概率为3% 3 月 12 日,据 CME「美联储观察」数据,美联储 3 月降息 25 个基点的概率为 3%,维持不变的概率为 97%。 2、美国劳工统计局将于今晚20:30公布2月CPI数据 3 月…...
蓝桥 2109统计子矩阵
问题描述 给定一个NM 的矩阵 A, 请你统计有多少个子矩阵 (最小 11, 最大 NM) 满足子矩阵中所有数的和不超过给定的整数 K ? 输入格式 第一行包含三个整数 N,M 和 K. 之后 NN 行每行包含 M 个整数, 代表矩阵 A. 输出格式 一个整数代表答案。 样例输入 3 4 10 1 2 3 4 5…...
Qt开源控件库(qt-material-widgets)的编译及使用
项目简介 qt-material-widgets是一个基于 Qt 小部件的 Material Design 规范实现。 项目地址 项目地址:qt-material-widgets 本地构建环境 Win11 家庭中文版 VS2019 Qt5.15.2 (MSVC2019) 本地构建流程 克隆后的目录结构如图: 直接使用Qt Crea…...
vue的 props 与 $emit 以及 provide 与 inject 的 组件之间的传值对比
好的,下面是 props 与 $emit 以及 provide 与 inject 的对比: 1. props 与 $emit props:父组件通过 props 向子组件传递数据,子组件接收后不可修改。子组件只能读取 props 传递给它的数据。如果需要修改或更新父组件的状态&#…...
用python批量生成文件夹
问题描述 当批量生成文件夹时,手动右键创建文件夹是一个繁琐的过程,尤其是文件夹的命名过程。假设从3月10日到3月19日,每天要为某个日常工作创建一个名为2025031x的文件夹,手动创建文件夹并命名费时费力。 百度给出了以下四种方法…...
Json 转义符号处理(Mongo changeStream op log)
使用mongo-kafka组件订阅mongo的changeStream得到 一个带有很多转义符号的json字符串 "{\"_id\": {\"_data\": \"8267D0F733000001502B022C0100296E5A1004366730C56F7E41A790BDA4CF23259A4F46645F6964006467B91713A024A00E32CDF6800004\"},…...
懒加载(Lazy Loading):原理、实现与优化策略
懒加载(Lazy Loading) 是一种优化网页性能的技术,主要用于延迟加载非关键资源(如图片、视频、脚本等),直到它们真正需要被使用时才加载。懒加载可以显著减少页面初始加载时间,降低带宽消耗&…...
dns劫持是什么?常见的劫持类型有哪些?如何预防?
DNS劫持的定义 DNS劫持(Domain Name System Hijacking)是一种网络攻击手段,攻击者通过篡改域名解析的过程,将用户对某个域名的访问请求重定向到错误或恶意的IP地址。这种攻击可能导致用户访问到钓鱼网站、恶意广告页面࿰…...
蓝桥杯省赛真题C++B组2024-握手问题
一、题目 【问题描述】 小蓝组织了一场算法交流会议,总共有 50 人参加了本次会议。在会议上,大家进行了握手交流。按照惯例他们每个人都要与除自己以外的其他所有人进行一次握手(且仅有一次)。但有 7 个人,这 7 人彼此之间没有进行握手(但这…...
【MySQL】基本操作 —— DDL
目录 DDLDDL 常用操作对数据库的常用操作查看所有数据库创建数据库切换、显示当前数据库删除数据库修改数据库编码 对表的常用操作创建表数据类型数值类型日期和时间类型字符串类型 查看当前数据库所有表查看指定表的创建语句查看指定表结构删除表 对表结构的常用操作给表添加字…...
XML语法
一、XML简介 (一)定义 XML(eXtensible Markup Language,可扩展标记语言)是一种简单的文本格式,用于标记电子文件使其具有结构性的标记语言。它与HTML(HyperText Markup Language,超…...
游戏引擎学习第152天
仓库:https://gitee.com/mrxiao_com/2d_game_3 回顾昨天的内容 这个节目展示了我们如何从零开始制作一款完整的游戏。我们不使用任何游戏引擎或库,而是从头开始创建一款游戏,整个开发过程都会呈现给大家。你将能够看到每一行代码的编写,了解…...
考研数学非数竞赛复习之Stolz定理求解数列极限
在非数类大学生数学竞赛中,Stolz定理作为一种强大的工具,经常被用来解决和式数列极限的问题,也被誉为离散版的’洛必达’方法,它提供了一种简洁而有效的方法,使得原本复杂繁琐的极限计算过程变得直观明了。本文&#x…...
故障诊断——neo4j入门
文章目录 neo4jQuickStartDemo neo4j QuickStart 详情可见博客:https://www.cnblogs.com/nhdlb/p/18703804,使用docker拉取最近的一个版本进行创建 docker run -it -d -p 7474:7474 -p 7687:7687 \ -v /disk5/neo4j_docker/data:/data \ -v /disk5/ne…...
【CXX】6.2 str — rust::Str
Rust::Str 公共 API // rust/cxx.hclass Str final { public:Str() noexcept;Str(const Str &) noexcept;Str(const String &) noexcept;// 如果输入不是 UTF-8,抛出 std::invalid_argument 异常。Str(const std::string &);Str(const char *);Str(con…...
【JavaWeb】快速入门——HTMLCSS
文章目录 一、 HTML简介1、HTML概念2、HTML文件结构3、可视化网页结构 二、 HTML标签语法1、标题标签2、段落标签3、超链接4、换行5、无序列表6、路径7、图片8、块1 盒子模型2 布局标签 三、 使用HTML表格展示数据1、定义表格2、合并单元格横向合并纵向合并 四、 使用HTML表单收…...
unordered_set 的常用函数
在 C 的标准库中,std::unordered_set 是基于哈希表实现的哈希集合。下面介绍这种语言里哈希集合的常用函数。 C std::unordered_set 1. 元素操作 insert 功能:向哈希集合中插入元素。如果元素已经存在,则不会重复插入。示例代码:…...
若依框架-给sys_user表添加新字段并获取当前登录用户的该字段值
目录 添加字段 修改SysUser类 修改SysUserMapper.xml 修改user.js 前端获取字段值 添加字段 若依框架的sys_user表是没有age字段的,但由于业务需求,我需要新添加一个age字段: 修改SysUser类 添加age字段后,要在SysUser类 …...
前端监测窗口尺寸和元素尺寸变化的方法
前端监测窗口尺寸变化和元素尺寸变化的方法 window.resize 简介 window.resize事件是浏览器提供的一种事件,用于监听窗口大小的改变。这意味着当用户调整浏览器窗口大小时,相关的JavaScript代码将被触发执行。这为开发者提供了一种机制,可…...
angular中下载接口返回文件
目录 一、URL.createObjectURL() 一、URL.createObjectURL() createObjectURL属于js的原生方法,位于window.URL上,用于将Blob或者File文件转换为可以临时的URL地址进行显示 **注意**:Angular 的 HttpClient 默认将响应解析为 JSON 对象16。…...
ubuntu 部署deepseek
更新 apt update 升级 apt upgrade 格式化硬盘 mkfs.ext4 /dev/sdb 安装nginx 查看端口 一、安装Ollama Ollama是一个开源的大型语言模型(LLM)推理服务器,为用户提供了灵活、安全和高性能的语言模型推理解决方案。 ollama/docs/linux.m…...
【每日学点HarmonyOS Next知识】拖拽调整列表顺序、tab回弹、自定义弹窗this、状态变量修饰枚举
1、HarmonyOS 功能实现(拖拽调整列表顺序)? 可参考: import curves from ohos.curves; import Curves from ohos.curvesEntry Component struct ListItemExample {State private arr: number[] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]…...
MySQL库和表的操作详解:从创建库到表的管理全面指南
目录 一、MySQL库的操作详解 〇、登录MySQL 一、数据库的创建与字符集设置 1. 创建数据库的语法 2. 创建数据库示例 查看创建出来的文件: bash下查看MySQL创建的文件 二、字符集与校验规则 1. 查看系统默认设置 2. 查看支持的字符集与校验规则 3. 校验规则对查询的影响…...
PyTorch 系列教程:使用CNN实现图像分类
图像分类是计算机视觉领域的一项基本任务,也是深度学习技术的一个常见应用。近年来,卷积神经网络(cnn)和PyTorch库的结合由于其易用性和鲁棒性已经成为执行图像分类的流行选择。 理解卷积神经网络(cnn) 卷…...
Docker下ARM64架构的源码编译Qt5.15.1,并移植到开发板上
Docker下ARM64架构的源码编译Qt5.15.1,并移植到开发板上 1、环境介绍 QT版本:5.15.1 待移植环境: jetson nano 系列开发板 aarch64架构(arm64) 编译环境: 虚拟机Ubuntu18.04(x86_64) 2、…...
