当前位置: 首页 > news >正文

MySQL之SQL 优化全攻略:从原理到实战

大家好呀!今天来跟大家好好聊聊 MySQL 的 SQL 优化~不管是开发还是运维,写得一手高效的 SQL 都是必备技能,毕竟谁也不想让系统卡成 PPT 对吧😂 这篇文章会从 MySQL 的底层逻辑讲到实际优化技巧,全是干货,赶紧码住!

目录

一、先搞懂 MySQL 的 "五脏六腑"—— 逻辑架构 🧠

1. 四层架构拆解

 2. 两大存储引擎 PK:MyISAM vs InnoDB 🆚

二、SQL 执行顺序:别被书写顺序骗了!📝

三、索引为啥快?揭秘 B+Tree 的 "黑科技" 🌳

1. 各种结构的 "坑"

2. B+Tree 的优势 ✅

四、Explain:SQL 的 "体检报告" 🩺

1. type:执行类型(越左越好)

2. extra:额外信息

3. id:查询序号

4. key:实际使用的索引

5. key_len:索引使用的字节长度

6. ref:索引关联的内容

五、SQL 优化实战:避开这些 "坑" 🚫

1. 全字段匹配最棒 ✨

2. 最佳左前缀法则 📏

3. 索引列别计算 🧮

4. 范围条件右边失效 ➡️

5. 别用%开头的 like ❌

6. 字符串加单引号 📌

7. 少用 or,用 union 替代 🤝

8. 覆盖索引:把 "目录" 变 "全书" 📚

9. 批量操作:少点外卖,多囤粮 🚚

10. 拒绝 SELECT *:只取所需,不搬全家 🏠

六、总结:优化口诀走一波 🌟


一、先搞懂 MySQL 的 "五脏六腑"—— 逻辑架构 🧠

MySQL 的逻辑架构就像一个工厂,每个环节各司其职,搞懂它才能明白优化的根源~

1. 四层架构拆解

  • 连接层:最上层的 "门卫",负责客户端连接(比如 TCP/IP、本地 sock),处理认证、线程池管理。比如我们用 Navicat 连数据库,就是在这一层建立连接啦~
  • 服务层:"核心车间",负责 SQL 的解析、优化、缓存等。比如 SQL 语句进来后,先由解析器处理,再由优化器找最优执行方案。
  • 引擎层:"存储引擎",数据怎么存、怎么取全看它!MySQL 支持多种引擎,最常用的是 MyISAM 和 InnoDB。
  • 存储层:"仓库",把数据存在磁盘上(比如 NTFS、NFS 文件系统)。

 2. 两大存储引擎 PK:MyISAM vs InnoDB 🆚

对比项MyISAMInnoDB
主外键不支持支持
事务不支持支持(ACID 特性)
锁机制表锁(操作一条记录锁全表)行锁(只锁操作的行)
缓存只缓存索引缓存索引 + 真实数据
表空间
适合场景读多写少(如博客列表)写多 / 需事务(如订单)

这两个引擎,MyISAM 注重性能,读取数据的速度非常的快,InnoDB注重事务,注重安全,读取速度比较慢。
我们企业中,一般会做一个MySQL主从复制   可以将主的MySQL服务器,配置为InnoDB, 从服务器可以配置为MyISAM ,因为它负责读。
可以做到读写分离。

👉 举个栗子:电商系统的订单表必须用 InnoDB(支持事务,防止下单时数据错乱);而商品列表页查询多,可用 MyISAM 提升读速度。实际中常搞 "主从复制":主库用 InnoDB(负责写),从库用 MyISAM(负责读),实现读写分离~

我们可以使用show engines的sql命令来查看当前数据库支持哪些存储引擎

二、SQL 执行顺序:别被书写顺序骗了!📝

我们写 SQL 的顺序是:SELECT → FROM → WHERE → GROUP BY → HAVING → ORDER BY,但 MySQL 执行时完全不一样!正确顺序是:

  1. FROM:先确定要查的表(比如从 user 表和 order 表开始)
  2. ON:处理表连接的条件(比如 user.id = order.user_id)
  3. JOIN:关联表(把符合 ON 条件的记录拼起来)
  4. WHERE:过滤行(比如 age > 18)
  5. GROUP BY:分组(比如按部门分组)
  6. HAVING:过滤组(比如分组后取平均工资 > 10k 的组)
  7. SELECT:提取字段(比如选 name、salary)
  8. DISTINCT:去重(比如去掉重复的 name)
  9. ORDER BY:排序(比如按 salary 降序)
  10. LIMIT:限制结果条数(比如取前 10 条)

👉 例子:查 "部门 30 中,工资 > 5k 的员工,按工资排序取前 3" 

SELECT DISTINCT name, salary 
FROM employee 
JOIN department ON employee.dept_id = department.id 
WHERE department.id = 30 AND salary > 5000 
GROUP BY name  -- 假设按姓名分组(实际可能按id更合理)
HAVING COUNT(*) = 1  -- 排除重复记录
ORDER BY salary DESC 
LIMIT 3;

执行时先关联表,再过滤部门和工资,分组后过滤,最后选字段、排序、限制条数~

三、索引为啥快?揭秘 B+Tree 的 "黑科技" 🌳

索引就像书的目录,能快速定位数据。但为啥 MySQL 偏爱 B+Tree 而不是其他结构?

1. 各种结构的 "坑"

  • Hash:快是快,但只支持等值查询(比如id=10),范围查询(id>10)直接歇菜❌
  • 二叉树 / 平衡二叉树:数据多了树会很高(比如 100 万数据可能要 20 层),查一次要读 20 次磁盘,太慢❌
  • B Tree:每个节点存索引 + 数据,导致一次读(16KB)能存的索引少,范围查询不方便❌

2. B+Tree 的优势 ✅

  • 非叶子节点只存索引,不存数据,16KB 能存更多索引(比如一次读 1000 个索引)
  • 叶子节点连起来(像链表),范围查询超方便(比如id>10 and id<20直接扫叶子节点)
  • 所有数据存在叶子节点,查询更稳定

👉 例子:查id between 100 and 200,B+Tree 直接定位到 100 的叶子节点,顺着链表读到 200,效率拉满!

四、Explain:SQL 的 "体检报告" 🩺

写了 SQL 不知道快不快?用EXPLAIN分析一下就知道!它能告诉你 SQL 的执行细节,重点看这几个字段:

1. type:执行类型(越左越好)

system > const > eq_ref > ref > range > index > all

  • System:表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也可以忽略不计,单表单记录可以出现。
  • const:主键 / 唯一索引查询,一次到位!比如select * from user where id=1 ✅
  • Eq_ref: 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描
  • ref:非唯一索引,返回匹配单值的多行。比如select * from user where name='张三'(name 是普通索引) ✅
  • range:范围查询(between、in、> 等)。比如select * from user where age between 18 and 30 ✅
  • Index: 称之为覆盖索引
  • all:全表扫描!数据量大时巨慢,必须优化❌(比如select * from user where age>18没建 age 索引)

2. extra:额外信息

  • Using filesort:排序用了磁盘文件,慢!(比如order by age但 age 没索引)❌
  • Using temporary:用了临时表,慢!(比如group by name但 name 没索引)❌
  • Using index:覆盖索引,只查索引不读数据,快!✅(比如索引是(name, age),查询select name, age from user where name='张三'

👉 例子:分析下面的 SQL

EXPLAIN SELECT * FROM user WHERE age > 18 ORDER BY name;

如果type=ALLextra=Using filesort,说明没建 age 索引,且 name 排序用了文件,得优化! 

3. id:查询序号

  • id 相同:执行顺序由上至下(比如多表连接时,先执行上面的表操作)。
  • id 不同:序号越大优先级越高,先执行(比如子查询,内层查询 id 更大,会先跑)。
    👉 例子:select * from user where id in (select user_id from order)中,子查询select user_id from order的 id 更大,会先执行。

4. key:实际使用的索引

  • 显示 SQL 真正用到的索引名称(如果为NULL,说明没用到索引)。
  • 注意:possible_keys是 "可能用到的索引",但key才是 "实际用到的",两者不一致时说明索引没被正确使用(比如索引失效)。
    👉 例子:possible_keys显示有age_idx,但keyNULL,说明查询时age_idx索引失效了(可能因为用了age+1>18这类计算)。

5. key_len:索引使用的字节长度

  • 表示索引字段使用的字节数,可判断索引是否被完整使用(长度越短,说明用到的索引字段越少,在满足需求的前提下越优)。
    👉 例子:复合索引(name, age),若key_len只计算了name的长度,说明age字段的索引没被用到。

6. ref:索引关联的内容

  • 表示索引是和什么关联的(比如常量、其他表的字段),反映索引的具体使用场景。
    👉 例子:ref=const说明是常量等值查询(如where id=1);ref=order.user_id说明是表连接时,用了order表的user_id字段关联索引。

五、SQL 优化实战:避开这些 "坑" 🚫

索引虽好,但一不小心就会失效,记住这些原则:

1. 全字段匹配最棒 ✨

创建索引(name, age, pos),查询where name='张三' and age=20 and pos='开发',索引全用上!

2. 最佳左前缀法则 📏

索引顺序是name→age→pos,查询时必须从左往右用,不能跳!

  • 有效:where name='张三'where name='张三' and age=20
  • 失效:where age=20(跳过 name)、where name='张三' and pos='开发'(跳过 age,pos 索引失效)

3. 索引列别计算 🧮

where id+1=10会让索引失效!改成where id=9

4. 范围条件右边失效 ➡️

where name='张三' and age>20 and pos='开发'中,age 是范围查询,后面的 pos 索引失效

5. 别用%开头的 like ❌

where name like '%张三'索引失效!改成where name like '张三%'(前缀匹配)

6. 字符串加单引号 📌

where name=123会隐式转换类型,索引失效!改成where name='123'

7. 少用 or,用 union 替代 🤝

where id=1 or id=2可能失效,改成:

select * from user where id=1
union
select * from user where id=2;

8. 覆盖索引:把 "目录" 变 "全书" 📚

如果查询的字段全部包含在索引中,就无需回表查询数据,这种索引称为覆盖索引
👉 例子:
创建复合索引(name, age),查询select name, age from user where name='张三'时,索引直接包含所有字段,查询速度飞起!
注意:覆盖索引要求索引包含SELECTWHEREORDER BY等子句中的所有字段。

9. 批量操作:少点外卖,多囤粮 🚚

  • 批量插入:用INSERT INTO ... VALUES (1,'a'),(2,'b')代替逐条插入,减少网络交互。
  • LOAD DATA INFILE:从文件导入数据,速度比INSERT快 10 倍以上!
LOAD DATA INFILE '/path/data.csv' INTO TABLE user;
  • INSERT SELECT:从其他表复制数据时,用一条语句搞定: 
INSERT INTO target_table (col1, col2) SELECT col1, col2 FROM source_table;

10. 拒绝 SELECT *:只取所需,不搬全家 🏠

  • 反例SELECT * FROM user会读取所有字段,包括大字段(如TEXT),增加 IO 和网络传输。
  • 正例SELECT id, name FROM user只取需要的字段,减少内存和带宽消耗。
    额外好处:使用覆盖索引时,SELECT *会导致回表,而明确字段可直接走索引。

六、总结:优化口诀走一波 🌟

全字段匹配好,左前缀要记牢;
索引不列计算,范围右边失效了;
% 开头 like 别用,字符串加引号;
or 换 union,explain 常检查;
type 避开 ALL,extra 无 filesort~

希望这篇文章能帮你写出飞一般的 SQL!有问题欢迎留言讨论哦~😊

http://www.dtcms.com/a/286363.html

相关文章:

  • 2025年华为认证之HCIE-云计算方向的报考流程
  • STM32之TB6612电机驱动模块
  • Oracle 11g RAC 高可用集群部署最佳实践
  • Level-MC 8”深暗之域“
  • 面向对象三大特性---继承
  • 大数据安全规划总体方案
  • 【深度学习】神经网络 批量标准化-part6
  • 在 OpenSUSE Tumbleweed 和 Leap 上安装 VirtualBox
  • Maven插件检测全路径名冲突如何开发Maven插件
  • AUTOSAR进阶图解==>AUTOSAR_SWS_E2ETransformer
  • 【加解密与C】Rot系列(二)Rot13
  • LVS(Linux virual server)
  • 【Git专栏】git如何切换到某个commit(超详细)
  • Java基础篇
  • Triton Inference Server 使用详解与常见问题汇总
  • 数据库模型异常问题深度解析:冗余与操作异常
  • 《在亚马逊平台运营:从专利困境到功能利用全指南》
  • 【数据结构】堆(下)+ 二叉树
  • 深度学习笔记002-引言--日常生活总的机器学习、训练模型的过程、关键组件、各种机器学习问题
  • 如何在服务器上获取Linux目录大小
  • 使用python读取json数据,简单的处理成元组数组
  • 验证大语言模型不会算数但可以编写算数的程序
  • 【数据结构初阶】--双向链表(一)
  • 机器学习17-Mamba
  • C语言—如何生成随机数+原理详细分析
  • Linux服务器端口被占用?
  • 无刷电机控制 - 基于STM32F405+CubeMX+HAL库+SimpleFOC04,完成霍尔传感器的驱动代码
  • @Scheduled的作用分析
  • 赛道观察:AI智能自习室哪家强?深挖深度逻辑与价值锚点
  • 链表算法之【链表的中间节点】