一、为什么数据库会变慢?
很多初学者认为:
数据库慢,就是 MySQL 性能不好。
实际上:
大多数问题来自:
SQL 和表设计不合理。
例如:
用户表:
user数据:
1000万条执行:
SELECT * FROM user WHERE username='Tom';数据库需要:
从第一条扫描到最后。
这个过程:
叫:
全表扫描(Full Table Scan)
二、什么是索引(Index)?
定义:
索引:
一种帮助数据库快速查询数据的数据结构。
类似:
书的目录。
没有索引:
看一本书:
逐页找:
第一页 第二页 第三页 ... 1000页有索引:
查目录:
直接定位:
第500页数据库:
没有索引:
逐行扫描有索引:
快速定位三、为什么索引能提高查询速度?
例如:
用户表:
100万数据。
字段:
id username没有索引:
查询:
WHERE username='Tom'流程:
第1行 ↓ 第2行 ↓ 第3行 ↓ ... 100万行复杂度:
O(n)
有索引:
索引树 Tom ↓ 数据位置 ↓ 直接读取接近:
O(log n)
四、MySQL索引结构:B+Tree ⭐⭐⭐⭐⭐
MySQL InnoDB 默认:
B+Tree。
结构:
类似:
50 / \ 20 80 / \ / \ 10 30 60 90查询:
查:
90
流程:
根节点 ↓ 80 ↓ 90不需要扫描所有数据。
五、为什么不用二叉树?
普通二叉树:
问题:
数据量大:
高度太高。
例如:
1000万数据:
二叉树:
可能:
几十层。
B+Tree:
一个节点:
保存大量数据。
高度低。
优势:
- 查询快
- 范围查询强
- 适合磁盘读取
六、索引类型
1. 主键索引
例如:
id BIGINT PRIMARY KEY自动创建。
特点:
唯一。
2. 普通索引
例如:
用户搜索:
username创建:
CREATE INDEX idx_username ON user(username);3. 唯一索引
例如:
邮箱:
email UNIQUE保证:
不能重复。
4. 联合索引
多个字段:
例如:
username status创建:
CREATE INDEX idx_user_status ON user(username,status);七、为什么企业需要联合索引?
场景:
后台用户列表:
条件:
WHERE username='Tom' AND status=1如果只有:
username索引。
查询:
先找到Tom。
再过滤status。
联合索引:
(username,status)直接定位。
效率更高。
八、最左匹配原则 ⭐⭐⭐⭐⭐
联合索引:
(username,status,age)有效:
WHERE username='Tom'✅
有效:
WHERE username='Tom' AND status=1✅
有效:
WHERE username='Tom' AND status=1 AND age=20✅
无效:
WHERE status=1❌
无效:
WHERE age=20❌
原因:
索引顺序:
从左开始。
九、什么情况下不要创建索引?
索引不是越多越好。
1. 数据量小
例如:
100条数据。
全表扫描更快。
2. 经常修改字段
例如:
状态字段:
status如果频繁修改:
维护索引成本增加。
3. 区分度低
例如:
性别:
male female只有两个值。
索引效果差。
十、EXPLAIN 执行计划 ⭐⭐⭐⭐⭐
优化SQL必须使用:
EXPLAIN例如:
EXPLAIN SELECT * FROM user WHERE username='Tom';查看:
数据库如何执行。
重点字段:
type
表示查询类型。
最好:
const eq_ref ref较差:
ALL表示:
全表扫描。
key
实际使用索引。
例如:
idx_username说明:
使用索引。
rows
扫描行数。
越少越好。
十一、慢 SQL 分析流程
企业常用:
流程:
发现慢SQL ↓ EXPLAIN ↓ 查看是否走索引 ↓ 优化SQL ↓ 增加索引 ↓ 重新测试例如:
慢:
SELECT * FROM orders WHERE user_id=100;检查:
没有索引:
type=ALL优化:
添加:
INDEX(user_id)再次:
EXPLAIN:
type=ref优化成功。
十二、分页优化 ⭐⭐⭐⭐⭐
普通分页:
LIMIT 100000,20问题:
MySQL:
先扫描:
100000条。
然后丢弃。
数据越大:
越慢。
例如:
第500万页。
优化方案1:ID分页
推荐:
SELECT * FROM user WHERE id>500000 LIMIT 20;利用:
主键索引。
速度快。
优化方案2:记录最后ID
例如:
第一页:
返回:
lastId=20第二页:
WHERE id>20互联网大量使用。
十三、大数据量查询优化
1. 不查询无用字段
不要:
SELECT *推荐:
SELECT id,name减少:
网络传输。
2. 避免深层JOIN
例如:
user join order join product join payment复杂。
拆开查询。
3. 热点数据缓存
例如:
商品详情:
访问频繁。
使用:
Redis。
十四、TypeORM 性能优化
1. 不滥用 relations
错误:
find({ relations:{ orders:true, roles:true, permissions:true } })可能:
生成巨大JOIN。
推荐:
按需查询。
2. 使用 select
不要:
find()全部字段。
推荐:
find({ select:{ id:true, username:true } })3. 分页必须限制
错误:
find()查询百万数据。
正确:
find({ skip:0, take:20 })4. 批量操作
错误:
循环:
for(){ save() }推荐:
批量:
save([ user1, user2, user3 ])十五、企业索引设计案例
用户表
user字段:
id username phone status create_time查询:
后台搜索:
WHERE username AND status索引:
INDEX(username,status)十六、订单表优化案例
订单:
orders id user_id status create_time常见:
查询用户订单:
WHERE user_id=100 ORDER BY create_time DESC索引:
INDEX( user_id, create_time )原因:
同时满足:
查询 + 排序。
十七、数据库优化原则总结
SQL层面
✅ 不使用SELECT *
✅ 避免全表扫描
✅ 合理分页
✅ 避免复杂JOIN
表设计
✅ 合理字段类型
✅ 合理索引
✅ 避免冗余数据
TypeORM
✅ 控制查询字段
✅ 合理分页
✅ 不滥用relations
✅ 批量操作
十八、本章总结表
| 知识 | 作用 |
|---|---|
| 索引 | 提高查询速度 |
| B+Tree | MySQL索引结构 |
| 联合索引 | 优化多条件查询 |
| 最左匹配 | 决定索引是否生效 |
| EXPLAIN | 分析SQL |
| 慢SQL | 定位性能问题 |
| ID分页 | 解决深分页 |
| select | 减少数据量 |
| relations | 避免巨大JOIN |
本章一句话总结:
数据库性能优化的核心不是让数据库更快,而是让数据库少做无用工作。