2
2
3
3
<!-- TOC depthFrom:2 depthTo:3 -->
4
4
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- 参考资料 )
20
20
21
21
<!-- /TOC -->
22
22
23
- ## 一、 数据结构优化
23
+ ## 1. 数据结构优化
24
24
25
25
良好的逻辑设计和物理设计是高性能的基石。
26
26
27
- ### 数据类型优化
27
+ ### 1.1. 数据类型优化
28
28
29
29
#### 数据类型优化基本原则
30
30
43
43
- 应该尽量避免用字符串类型作为标识列,因为它们很消耗空间,并且通常比数字类型慢。对于 ` MD5 ` 、` SHA ` 、` UUID ` 这类随机字符串,由于比较随机,所以可能分布在很大的空间内,导致 ` INSERT ` 以及一些 ` SELECT ` 语句变得很慢。
44
44
- 如果存储 UUID ,应该移除 ` - ` 符号;更好的做法是,用 ` UNHEX() ` 函数转换 UUID 值为 16 字节的数字,并存储在一个 ` BINARY(16) ` 的列中,检索时,可以通过 ` HEX() ` 函数来格式化为 16 进制格式。
45
45
46
- ### 表设计
46
+ ### 1.2. 表设计
47
47
48
48
应该避免的设计问题:
49
49
52
52
- ** 枚举** - 尽量不要用枚举,因为添加和删除字符串(枚举选项)必须使用 ` ALTER TABLE ` 。
53
53
- 尽量避免 ` NULL `
54
54
55
- ### 范式和反范式
55
+ ### 1.3. 范式和反范式
56
56
57
57
** 范式化目标是尽量减少冗余,而反范式化则相反** 。
58
58
68
68
69
69
在真实世界中,很少会极端地使用范式化或反范式化。实际上,应该权衡范式和反范式的利弊,混合使用。
70
70
71
- ### 索引优化
71
+ ### 1.4. 索引优化
72
72
73
73
> 索引优化应该是查询性能优化的最有效手段。
74
74
>
97
97
- ** 覆盖索引**
98
98
- ** 自增字段作主键**
99
99
100
- ## 二、 SQL 优化
100
+ ## 2. SQL 优化
101
101
102
- SQL 优化后,可以通过执行计划 (` EXPLAIN ` )来查看优化效果。
102
+ 使用 ` EXPLAIN ` 命令查看当前 SQL 是否使用了索引, 优化后,再通过执行计划 (` EXPLAIN ` )来查看优化效果。
103
103
104
104
SQL 优化基本思路:
105
105
106
106
- ** 只返回必要的列** - 最好不要使用 ` SELECT * ` 语句。
107
107
108
- - ** 只返回必要的行** - 使用 WHERE 语句进行查询过滤 ,有时候也需要使用 LIMIT 语句来限制返回的数据。
108
+ - ** 只返回必要的行** - 使用 ` WHERE ` 子查询语句进行过滤查询 ,有时候也需要使用 ` LIMIT ` 语句来限制返回的数据。
109
109
110
110
- ** 缓存重复查询的数据** - 应该考虑在客户端使用缓存,尽量不要使用 Mysql 服务器缓存(存在较多问题和限制)。
111
111
112
112
- ** 使用索引来覆盖查询**
113
113
114
- ### 优化 COUNT() 查询
114
+ ### 2.1. 优化 ` COUNT() ` 查询
115
115
116
116
` COUNT() ` 有两种作用:
117
117
@@ -135,7 +135,7 @@ FROM world.city WHERE id <= 5;
135
135
136
136
有时候某些业务场景并不需要完全精确的统计值,可以用近似值来代替,` EXPLAIN ` 出来的行数就是一个不错的近似值,而且执行 ` EXPLAIN ` 并不需要真正地去执行查询,所以成本非常低。通常来说,执行 ` COUNT() ` 都需要扫描大量的行才能获取到精确的数据,因此很难优化,MySQL 层面还能做得也就只有覆盖索引了。如果不还能解决问题,只有从架构层面解决了,比如添加汇总表,或者使用 Redis 这样的外部缓存系统。
137
137
138
- ### 优化关联查询
138
+ ### 2.2. 优化关联查询
139
139
140
140
在大数据场景下,表与表之间通过一个冗余字段来关联,要比直接使用 ` JOIN ` 有更好的性能。
141
141
@@ -172,11 +172,11 @@ while(outer_row) {
172
172
173
173
可以看到,最外层的查询是根据` A.xx ` 列来查询的,` A.c ` 上如果有索引的话,整个关联查询也不会使用。再看内层的查询,很明显` B.c ` 上如果有索引的话,能够加速查询,因此只需要在关联顺序中的第二张表的相应列上创建索引即可。
174
174
175
- ### 优化 GROUP BY 和 DISTINCT
175
+ ### 2.3. 优化 ` GROUP BY ` 和 ` DISTINCT `
176
176
177
177
Mysql 优化器会在内部处理的时候相互转化这两类查询。它们都** 可以使用索引来优化,这也是最有效的优化方法** 。
178
178
179
- ### 优化 LIMIT
179
+ ### 2.4. 优化 ` LIMIT `
180
180
181
181
当需要分页操作时,通常会使用 ` LIMIT ` 加上偏移量的办法实现,同时加上合适的 ` ORDER BY ` 字句。** 如果有对应的索引,通常效率会不错,否则,MySQL 需要做大量的文件排序操作** 。
182
182
@@ -209,13 +209,13 @@ SELECT id FROM t WHERE id > 10000 LIMIT 10;
209
209
210
210
其他优化的办法还包括使用预先计算的汇总表,或者关联到一个冗余表,冗余表中只包含主键列和需要做排序的列。
211
211
212
- ### 优化 UNION
212
+ ### 2.5. 优化 UNION
213
213
214
214
MySQL 总是通过创建并填充临时表的方式来执行 ` UNION ` 查询。因此很多优化策略在` UNION ` 查询中都没有办法很好的时候。经常需要手动将` WHERE ` 、` LIMIT ` 、` ORDER BY ` 等字句“下推”到各个子查询中,以便优化器可以充分利用这些条件先优化。
215
215
216
216
除非确实需要服务器去重,否则就一定要使用` UNION ALL ` ,如果没有` ALL ` 关键字,MySQL 会给临时表加上` DISTINCT ` 选项,这会导致整个临时表的数据做唯一性检查,这样做的代价非常高。当然即使使用 ALL 关键字,MySQL 总是将结果放入临时表,然后再读出,再返回给客户端。虽然很多时候没有这个必要,比如有时候可以直接把每个子查询的结果返回给客户端。
217
217
218
- ### 优化查询方式
218
+ ### 2.6. 优化查询方式
219
219
220
220
#### 切分大查询
221
221
@@ -256,11 +256,11 @@ SELECT * FROM tag_post WHERE tag_id=1234;
256
256
SELECT * FROM post WHERE post .id IN (123 ,456 ,567 ,9098 ,8904 );
257
257
```
258
258
259
- ## 三、 EXPLAIN
259
+ ## 3. 执行计划( ` EXPLAIN ` )
260
260
261
- 如何检验修改后的 SQL 确实有优化效果?这就需要用到执行计划( ` EXPLAIN ` )。
261
+ 如何判断当前 SQL 是否使用了索引? 如何检验修改后的 SQL 确实有优化效果?
262
262
263
- 使用执行计划 ` EXPLAIN ` 用来分析 ` SELECT ` 查询效率,开发人员可以通过分析 ` EXPLAIN ` 结果来优化查询语句 。
263
+ 在 SQL 中,可以通过执行计划( ` EXPLAIN ` )分析 ` SELECT ` 查询效率。
264
264
265
265
``` sql
266
266
mysql> explain select * from user_info where id = 2 \G
@@ -280,30 +280,36 @@ possible_keys: PRIMARY
280
280
1 row in set , 1 warning (0 .00 sec)
281
281
```
282
282
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 ` :额外的信息。
303
309
304
310
> 更多内容请参考:[ MySQL 性能优化神器 Explain 使用分析] ( https://segmentfault.com/a/1190000008131735 )
305
311
306
- ## 四、 optimizer trace
312
+ ## 4. optimizer trace
307
313
308
314
在 MySQL 5.6 及之后的版本中,我们可以使用 optimizer trace 功能查看优化器生成执行计划的整个过程。有了这个功能,我们不仅可以了解优化器的选择过程,更可以了解每一个执行环节的成本,然后依靠这些信息进一步优化查询。
309
315
@@ -316,7 +322,7 @@ SELECT * FROM information_schema.OPTIMIZER_TRACE;
316
322
SET optimizer_trace="enabled=off";
317
323
```
318
324
319
- ## 参考资料
325
+ ## 5. 参考资料
320
326
321
327
- [《高性能 MySQL》](https://book.douban.com/subject/23008813/)
322
328
- [Java 性能调优实战](https://time.geekbang.org/column/intro/100028001)
0 commit comments