第三十九天学习心得

今天正式进入了SQL优化的学习。如果说之前学习MySQL是在学习"如何用",那么今天就是在学习"如何用好"。从数据结构到索引原理,从慢查询定位到SQL调优,一整天的内容环环相扣,让我对数据库性能优化有了系统性的认知。

一、数据结构------索引的底层逻辑

课程从数据结构讲起,这是我之前学MySQL时完全忽略的视角。**数据结构的差异决定了索引的性能优劣**,理解数据结构才能真正理解为什么MySQL选择B+Tree。

从数组到红黑树

| 数据结构 | 查询复杂度 | 增删复杂度 | 特点 |

|----------|-----------|-----------|------|

| 数组 | O(1) | O(n) | 连续内存,下标访问快 |

| 链表 | O(n) | O(1) | 非连续内存,增删快 |

| 跳表 | O(log n) | O(log n) | 多级索引,Redis的zset使用 |

| 平衡二叉树AVL | O(log n) | O(log n) | 严格平衡,调整代价大 |

| 红黑树 | O(log n) | O(log n) | 弱平衡,增删性能优于AVL |

红黑树的5条规则让我印象深刻:节点非红即黑、根黑叶黑、红节点子节点必须为黑、从任一节点到叶子的路径黑色节点数相同。最后一条规则保证了**最长路径不超过最短路径的2倍**,这是红黑树"弱平衡"的核心。

B-Tree与B+Tree------数据库的真正选择

B-Tree(B树,不是B减树)是多路平衡搜索树,每个节点既存关键字也存数据,所有叶子在同一层。

B+Tree是B-Tree的变种,核心区别在于:

内部节点只存关键字和指针,不存数据------页面可以容纳更多关键字,树更矮

所有数据都存储在叶子节点------查询必须走到叶子,性能稳定

叶子节点之间用指针相连------范围查询极其高效

MySQL选择B+Tree而非B-Tree的原因:

  1. 磁盘I/O更少:树矮,每次IO加载更多关键字

  2. 范围查询更强:叶子链表支持顺序遍历

  3. 查询性能稳定:每次都要走到叶子层,IO次数固定

  4. 适合磁盘存储:节点大小与磁盘页大小匹配

二、存储引擎与索引分类

InnoDB vs MyISAM

| 对比维度 | InnoDB | MyISAM |

|----------|--------|--------|

| 事务 | ✅ 支持 | ❌ 不支持 |

| 外键 | ✅ 支持 | ❌ 不支持 |

| 锁粒度 | 行级锁 | 表锁 |

| 索引结构 | 聚集索引(数据+索引一起) | 非聚集索引(数据和索引分离) |

MySQL 5.5之后InnoDB成为默认引擎,事务支持和行级锁使其在高并发场景下表现更优。

聚集索引 vs 非聚集索引

聚集索引:数据和索引在一起,叶子节点存放完整行数据。主键就是聚集索引,没有主键则用唯一索引,都没有则自动生成隐藏列。

非聚集索引(二级索引):叶子节点存放主键值。通过非聚集索引查询需要**回表**------先查二级索引拿主键,再到聚集索引拿完整数据。

三、性能分析工具

SQL频率监控

```sql

SHOW GLOBAL STATUS LIKE 'Com______'; -- 7个下划线

查看数据库的DML操作频率,判断是否需要优化。

慢查询日志

开启慢查询日志,记录执行时间超过阈值的SQL:

properties

slow_query_log=1

long_query_time=2

SHOW PROFILES

`sql

SET profiling=1;

SHOW PROFILES;

SHOW PROFILE CPU FOR QUERY 3;

分析SQL的CPU消耗分布:

cpu_user:用户模式消耗,表示查询逻辑计算密集

cpu_system:系统模式消耗,表示I/O或系统调用密集

EXPLAIN ------ 最核心的分析工具

EXPLAIN的字段中,type是最关键的指标,性能从好到差:

NULL → system → const → eq_ref → ref → range → index → all

| type | 含义 | 优化建议 |

|------|------|----------|

| const | 主键或唯一索引查询 | ✅ 最优 |

| eq_ref | 唯一索引关联查询 | ✅ 很好 |

| ref | 非唯一索引查询 | ⚠️ 可接受 |

| range | 范围查询 | ⚠️ 可接受 |

| index | 扫描整个索引树 | ❌ 需优化 |

| all | 全表扫描 | ❌ 必须优化 |

目标是让type达到range级别以上。

四、索引使用规则

最左前缀法则

复合索引 `(name, age, major)` 中,查询条件必须包含最左列 `name` 才能使用索引。

```sql

-- ✅ 使用索引

WHERE name = '亚瑟' AND age = 20 AND major = '土木'

WHERE name = '亚瑟' AND age = 20

-- ❌ 索引失效(未使用最左列)

WHERE age = 20 AND major = '土木'

```

跨列使用会导致部分索引失效:`name` 和 `age` 之间有 `age`,跳过 `age` 直接查 `major`,只有 `name` 走索引。

范围查询

复合索引中,一旦出现 `>`、`<`、`BETWEEN`、`LIKE`(前缀模糊),**范围字段右边的所有条件全部失效**。

```sql

-- ❌ major失效(age用了范围查询)

WHERE name = '亚瑟' AND age > 20 AND major = '土木'

-- ✅ 三个字段都用上索引

WHERE name = '亚瑟' AND age >= 20 AND major = '土木'

索引失效的常见场景

  1. 对索引列做计算:`WHERE age + 10 = 30` → 失效

  2. 字符串列用数字查询:`WHERE code = 10001`(code是varchar)→ 失效

  3. LIKE以%开头:`WHERE name LIKE '%亚瑟'` → 失效

  4. OR导致索引失效:两边条件都有索引才可能走索引,否则全表扫描

覆盖索引

覆盖索引是指查询需要的字段都能在索引中找到,不需要回表。Extra显示`Using index`时表示使用了覆盖索引。

```sql

-- ✅ 覆盖索引(id和name都在索引中)

SELECT id, name FROM students WHERE name = '亚瑟'

-- ❌ 需要回表(major不在索引中)

SELECT id, name, major FROM students WHERE name = '亚瑟'

尽量不要用 `SELECT *`,只查需要的字段,这既是规范也是优化。

五、前缀索引

对于长字符串列(如email、长文本),建立完整索引会导致索引文件过大。**前缀索引**只对字符串的前N个字符建立索引:

```sql

CREATE INDEX email_index ON students(email(5));

选择性的概念:不重复记录数 / 总记录数,值越接近1越好。通过计算不同前缀长度的选择性,找到合适的长度。

六、SQL优化实践

INSERT优化

使用批量插入(建议一次不超过1000条)

手动提交事务(多条INSERT用START TRANSACTION + COMMIT)

主键顺序插入,避免页分裂

使用LOAD命令加载本地文件

主键设计原则

尽量降低主键长度

主键值保持不变

避免使用UUID作为主键

使用自增主键保证顺序插入

ORDER BY优化

目标是从`Using filesort`优化到`Using index`:

为排序字段建立索引

使用覆盖索引避免回表

复合索引排序时遵循最左前缀

升降序方向需与索引定义一致

GROUP BY优化

建立索引提升分组效率

遵循最左前缀法则

Extra出现`Using temporary`说明使用了临时表,需要优化

LIMIT优化

大偏移量查询慢的根本原因是MySQL需要跳过大量行:

```sql

-- ❌ 慢:需要处理100010行再抛弃100000行

SELECT * FROM students LIMIT 100000, 10

-- ✅ 优化:先查id,再关联

SELECT * FROM students s

INNER JOIN (SELECT id FROM students LIMIT 100000, 10) ss

ON ss.id = s.id

COUNT优化

理论上效率:`count(普通列)` < `count(id)` < `count(1)` ≈ `count(*)`

实际受版本和存储引擎影响,课堂上实测`count(name)`竟然比`count(*)`更快------说明理论需要结合实际。

七、今日总结

今天的SQL优化学习让我从"能写SQL"进入了"能写好SQL"的阶段:

| 知识点 | 核心要点 |

|--------|----------|

| 数据结构 | B+Tree是MySQL索引的基石 |

| 存储引擎 | InnoDB支持事务+行锁,MyISAM只读性能好 |

| 聚集索引 | 数据+索引在一起,回表是性能杀手 |

| EXPLAIN | type是关键指标,目标是range及以上 |

| 最左前缀 | 复合索引必须包含最左列 |

| 覆盖索引 | 减少回表,Extra显示Using index |

| 前缀索引 | 长字符串列节省空间 |

三点感悟:

  1. **知其然更要知其所以然**:以前只知道"索引能加速查询",今天终于理解了B+Tree的结构和MySQL选择它的原因。

  2. **EXPLAIN是SQL优化的起点**:写SQL之前先用EXPLAIN看一下执行计划,type为all的SQL一定要优化。

  3. 索引不是越多越好:索引能加速查询,但会拖慢增删改。单表索引数建议不超过5个,复合索引设计要合理。

明天也继续加油

相关推荐
水巷石子1 小时前
学习langChain4j的第二天,体验springBoot中的starter
java·spring boot·学习·spring·langchain4j
程序员梅雨2 小时前
微服务学习最终篇: Elasticsearch
学习·elasticsearch·微服务
clorinda2 小时前
PyTorch 手写数字识别学习笔记
pytorch·笔记·学习
m4Rk_2 小时前
【论文阅读】Agent 记忆机制(63):SAMem——把经验记忆细化为带长期价值的状态—Thought
论文阅读·人工智能·学习·开源·github
kyrie_sakura2 小时前
MySQL数据库学习笔记1 -- 基础概念,sql语句
数据库·学习·mysql
吴声子夜歌3 小时前
Linux命令——学习Shell(三)
linux·chrome·学习
小猪佩奇TONY3 小时前
Linux 内核学习(18) --- linux dma_buf 机制
linux·运维·学习
叶子野格3 小时前
《Verilog学习:数字系统设计基础》
学习·fpga开发
边境悍匪3 小时前
蜗牛学苑 Java 智能体学习 Day36|阿里云 OSS 文件上传、Spring 事务、全局异常思维导图复盘
java·学习·阿里云