Skip to content

Commit 846bd3b

Browse files
committed
update docs
1 parent 961094a commit 846bd3b

File tree

2 files changed

+102
-91
lines changed

2 files changed

+102
-91
lines changed

docs/sql/mysql/mysql-optimization.md

Lines changed: 60 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,29 @@
22

33
<!-- TOC depthFrom:2 depthTo:3 -->
44

5-
- [一、数据结构优化](#一数据结构优化)
6-
- [数据类型优化](#数据类型优化)
7-
- [表设计](#表设计)
8-
- [范式和反范式](#范式和反范式)
9-
- [索引优化](#索引优化)
10-
- [二、SQL 优化](#二sql-优化)
11-
- [优化 COUNT() 查询](#优化-count-查询)
12-
- [优化关联查询](#优化关联查询)
13-
- [优化 GROUP BY 和 DISTINCT](#优化-group-by-和-distinct)
14-
- [优化 LIMIT](#优化-limit)
15-
- [优化 UNION](#优化-union)
16-
- [优化查询方式](#优化查询方式)
17-
- [三、EXPLAIN](#三explain)
18-
- [四、optimizer trace](#四optimizer-trace)
19-
- [参考资料](#参考资料)
5+
- [1. 数据结构优化](#1-数据结构优化)
6+
- [1.1. 数据类型优化](#11-数据类型优化)
7+
- [1.2. 表设计](#12-表设计)
8+
- [1.3. 范式和反范式](#13-范式和反范式)
9+
- [1.4. 索引优化](#14-索引优化)
10+
- [2. SQL 优化](#2-sql-优化)
11+
- [2.1. 优化 `COUNT()` 查询](#21-优化-count-查询)
12+
- [2.2. 优化关联查询](#22-优化关联查询)
13+
- [2.3. 优化 `GROUP BY``DISTINCT`](#23-优化-group-by-和-distinct)
14+
- [2.4. 优化 `LIMIT`](#24-优化-limit)
15+
- [2.5. 优化 UNION](#25-优化-union)
16+
- [2.6. 优化查询方式](#26-优化查询方式)
17+
- [3. 执行计划(`EXPLAIN`](#3-执行计划explain)
18+
- [4. optimizer trace](#4-optimizer-trace)
19+
- [5. 参考资料](#5-参考资料)
2020

2121
<!-- /TOC -->
2222

23-
## 一、数据结构优化
23+
## 1. 数据结构优化
2424

2525
良好的逻辑设计和物理设计是高性能的基石。
2626

27-
### 数据类型优化
27+
### 1.1. 数据类型优化
2828

2929
#### 数据类型优化基本原则
3030

@@ -43,7 +43,7 @@
4343
- 应该尽量避免用字符串类型作为标识列,因为它们很消耗空间,并且通常比数字类型慢。对于 `MD5``SHA``UUID` 这类随机字符串,由于比较随机,所以可能分布在很大的空间内,导致 `INSERT` 以及一些 `SELECT` 语句变得很慢。
4444
- 如果存储 UUID ,应该移除 `-` 符号;更好的做法是,用 `UNHEX()` 函数转换 UUID 值为 16 字节的数字,并存储在一个 `BINARY(16)` 的列中,检索时,可以通过 `HEX()` 函数来格式化为 16 进制格式。
4545

46-
### 表设计
46+
### 1.2. 表设计
4747

4848
应该避免的设计问题:
4949

@@ -52,7 +52,7 @@
5252
- **枚举** - 尽量不要用枚举,因为添加和删除字符串(枚举选项)必须使用 `ALTER TABLE`
5353
- 尽量避免 `NULL`
5454

55-
### 范式和反范式
55+
### 1.3. 范式和反范式
5656

5757
**范式化目标是尽量减少冗余,而反范式化则相反**
5858

@@ -68,7 +68,7 @@
6868

6969
在真实世界中,很少会极端地使用范式化或反范式化。实际上,应该权衡范式和反范式的利弊,混合使用。
7070

71-
### 索引优化
71+
### 1.4. 索引优化
7272

7373
> 索引优化应该是查询性能优化的最有效手段。
7474
>
@@ -97,21 +97,21 @@
9797
- **覆盖索引**
9898
- **自增字段作主键**
9999

100-
## 二、SQL 优化
100+
## 2. SQL 优化
101101

102-
SQL 优化后,可以通过执行计划`EXPLAIN`)来查看优化效果。
102+
使用 `EXPLAIN` 命令查看当前 SQL 是否使用了索引,优化后,再通过执行计划`EXPLAIN`)来查看优化效果。
103103

104104
SQL 优化基本思路:
105105

106106
- **只返回必要的列** - 最好不要使用 `SELECT *` 语句。
107107

108-
- **只返回必要的行** - 使用 WHERE 语句进行查询过滤,有时候也需要使用 LIMIT 语句来限制返回的数据。
108+
- **只返回必要的行** - 使用 `WHERE` 子查询语句进行过滤查询,有时候也需要使用 `LIMIT` 语句来限制返回的数据。
109109

110110
- **缓存重复查询的数据** - 应该考虑在客户端使用缓存,尽量不要使用 Mysql 服务器缓存(存在较多问题和限制)。
111111

112112
- **使用索引来覆盖查询**
113113

114-
### 优化 COUNT() 查询
114+
### 2.1. 优化 `COUNT()` 查询
115115

116116
`COUNT()` 有两种作用:
117117

@@ -135,7 +135,7 @@ FROM world.city WHERE id <= 5;
135135

136136
有时候某些业务场景并不需要完全精确的统计值,可以用近似值来代替,`EXPLAIN` 出来的行数就是一个不错的近似值,而且执行 `EXPLAIN` 并不需要真正地去执行查询,所以成本非常低。通常来说,执行 `COUNT()` 都需要扫描大量的行才能获取到精确的数据,因此很难优化,MySQL 层面还能做得也就只有覆盖索引了。如果不还能解决问题,只有从架构层面解决了,比如添加汇总表,或者使用 Redis 这样的外部缓存系统。
137137

138-
### 优化关联查询
138+
### 2.2. 优化关联查询
139139

140140
在大数据场景下,表与表之间通过一个冗余字段来关联,要比直接使用 `JOIN` 有更好的性能。
141141

@@ -172,11 +172,11 @@ while(outer_row) {
172172

173173
可以看到,最外层的查询是根据`A.xx`列来查询的,`A.c`上如果有索引的话,整个关联查询也不会使用。再看内层的查询,很明显`B.c`上如果有索引的话,能够加速查询,因此只需要在关联顺序中的第二张表的相应列上创建索引即可。
174174

175-
### 优化 GROUP BY 和 DISTINCT
175+
### 2.3. 优化 `GROUP BY``DISTINCT`
176176

177177
Mysql 优化器会在内部处理的时候相互转化这两类查询。它们都**可以使用索引来优化,这也是最有效的优化方法**
178178

179-
### 优化 LIMIT
179+
### 2.4. 优化 `LIMIT`
180180

181181
当需要分页操作时,通常会使用 `LIMIT` 加上偏移量的办法实现,同时加上合适的 `ORDER BY` 字句。**如果有对应的索引,通常效率会不错,否则,MySQL 需要做大量的文件排序操作**
182182

@@ -209,13 +209,13 @@ SELECT id FROM t WHERE id > 10000 LIMIT 10;
209209

210210
其他优化的办法还包括使用预先计算的汇总表,或者关联到一个冗余表,冗余表中只包含主键列和需要做排序的列。
211211

212-
### 优化 UNION
212+
### 2.5. 优化 UNION
213213

214214
MySQL 总是通过创建并填充临时表的方式来执行 `UNION` 查询。因此很多优化策略在`UNION`查询中都没有办法很好的时候。经常需要手动将`WHERE``LIMIT``ORDER BY`等字句“下推”到各个子查询中,以便优化器可以充分利用这些条件先优化。
215215

216216
除非确实需要服务器去重,否则就一定要使用`UNION ALL`,如果没有`ALL`关键字,MySQL 会给临时表加上`DISTINCT`选项,这会导致整个临时表的数据做唯一性检查,这样做的代价非常高。当然即使使用 ALL 关键字,MySQL 总是将结果放入临时表,然后再读出,再返回给客户端。虽然很多时候没有这个必要,比如有时候可以直接把每个子查询的结果返回给客户端。
217217

218-
### 优化查询方式
218+
### 2.6. 优化查询方式
219219

220220
#### 切分大查询
221221

@@ -256,11 +256,11 @@ SELECT * FROM tag_post WHERE tag_id=1234;
256256
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);
257257
```
258258

259-
## 三、EXPLAIN
259+
## 3. 执行计划(`EXPLAIN`
260260

261-
如何检验修改后的 SQL 确实有优化效果?这就需要用到执行计划(`EXPLAIN`)。
261+
如何判断当前 SQL 是否使用了索引?如何检验修改后的 SQL 确实有优化效果?
262262

263-
使用执行计划 `EXPLAIN` 用来分析 `SELECT` 查询效率,开发人员可以通过分析 `EXPLAIN` 结果来优化查询语句
263+
在 SQL 中,可以通过执行计划(`EXPLAIN`)分析 `SELECT` 查询效率。
264264

265265
```sql
266266
mysql> explain select * from user_info where id = 2\G
@@ -280,30 +280,36 @@ possible_keys: PRIMARY
280280
1 row in set, 1 warning (0.00 sec)
281281
```
282282

283-
各列含义如下:
284-
285-
- id: SELECT 查询的标识符. 每个 SELECT 都会自动分配一个唯一的标识符.
286-
- select_type: SELECT 查询的类型.
287-
- SIMPLE, 表示此查询不包含 UNION 查询或子查询
288-
- PRIMARY, 表示此查询是最外层的查询
289-
- UNION, 表示此查询是 UNION 的第二或随后的查询
290-
- DEPENDENT UNION, UNION 中的第二个或后面的查询语句, 取决于外面的查询
291-
- UNION RESULT, UNION 的结果
292-
- SUBQUERY, 子查询中的第一个 SELECT
293-
- DEPENDENT SUBQUERY: 子查询中的第一个 SELECT, 取决于外面的查询. 即子查询依赖于外层查询的结果.
294-
- table: 查询的是哪个表
295-
- partitions: 匹配的分区
296-
- type: join 类型
297-
- possible_keys: 此次查询中可能选用的索引
298-
- key: 此次查询中确切使用到的索引.
299-
- ref: 哪个字段或常数与 key 一起被使用
300-
- rows: 显示此查询一共扫描了多少行. 这个是一个估计值.
301-
- filtered: 表示此查询条件所过滤的数据的百分比
302-
- extra: 额外的信息
283+
`EXPLAIN` 参数说明:
284+
285+
- `id`: SELECT 查询的标识符. 每个 SELECT 都会自动分配一个唯一的标识符.
286+
- `select_type` ⭐ :SELECT 查询的类型.
287+
- `SIMPLE`:表示此查询不包含 UNION 查询或子查询
288+
- `PRIMARY`:表示此查询是最外层的查询
289+
- `UNION`:表示此查询是 UNION 的第二或随后的查询
290+
- `DEPENDENT UNION`:UNION 中的第二个或后面的查询语句, 取决于外面的查询
291+
- `UNION RESULT`:UNION 的结果
292+
- `SUBQUERY`:子查询中的第一个 SELECT
293+
- `DEPENDENT SUBQUERY`: 子查询中的第一个 SELECT, 取决于外面的查询. 即子查询依赖于外层查询的结果.
294+
- `table`: 查询的是哪个表,如果给表起别名了,则显示别名。
295+
- `partitions`:匹配的分区
296+
- `type` ⭐:表示从表中查询到行所执行的方式,查询方式是 SQL 优化中一个很重要的指标,结果值从好到差依次是:system > const > eq_ref > ref > range > index > ALL。
297+
- `system`/`const`:表中只有一行数据匹配,此时根据索引查询一次就能找到对应的数据。如果是 B + 树索引,我们知道此时索引构造成了多个层级的树,当查询的索引在树的底层时,查询效率就越低。const 表示此时索引在第一层,只需访问一层便能得到数据。
298+
- `eq_ref`:使用唯一索引扫描,常见于多表连接中使用主键和唯一索引作为关联条件。
299+
- `ref`:非唯一索引扫描,还可见于唯一索引最左原则匹配扫描。
300+
- `range`:索引范围扫描,比如,<,>,between 等操作。
301+
- `index`:索引全表扫描,此时遍历整个索引树。
302+
- `ALL`:表示全表扫描,需要遍历全表来找到对应的行。
303+
- `possible_keys`:此次查询中可能选用的索引。
304+
- `key` ⭐:此次查询中实际使用的索引。
305+
- `ref`:哪个字段或常数与 key 一起被使用。
306+
- `rows` ⭐:显示此查询一共扫描了多少行,这个是一个估计值。
307+
- `filtered`:表示此查询条件所过滤的数据的百分比。
308+
- `extra`:额外的信息。
303309

304310
> 更多内容请参考:[MySQL 性能优化神器 Explain 使用分析](https://segmentfault.com/a/1190000008131735)
305311
306-
## 四、optimizer trace
312+
## 4. optimizer trace
307313

308314
在 MySQL 5.6 及之后的版本中,我们可以使用 optimizer trace 功能查看优化器生成执行计划的整个过程。有了这个功能,我们不仅可以了解优化器的选择过程,更可以了解每一个执行环节的成本,然后依靠这些信息进一步优化查询。
309315

@@ -316,7 +322,7 @@ SELECT * FROM information_schema.OPTIMIZER_TRACE;
316322
SET optimizer_trace="enabled=off";
317323
```
318324
319-
## 参考资料
325+
## 5. 参考资料
320326
321327
- [《高性能 MySQL》](https://book.douban.com/subject/23008813/)
322328
- [Java 性能调优实战](https://time.geekbang.org/column/intro/100028001)

0 commit comments

Comments
 (0)