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
4343- 应该尽量避免用字符串类型作为标识列,因为它们很消耗空间,并且通常比数字类型慢。对于 ` MD5 ` 、` SHA ` 、` UUID ` 这类随机字符串,由于比较随机,所以可能分布在很大的空间内,导致 ` INSERT ` 以及一些 ` SELECT ` 语句变得很慢。
4444 - 如果存储 UUID ,应该移除 ` - ` 符号;更好的做法是,用 ` UNHEX() ` 函数转换 UUID 值为 16 字节的数字,并存储在一个 ` BINARY(16) ` 的列中,检索时,可以通过 ` HEX() ` 函数来格式化为 16 进制格式。
4545
46- ### 表设计
46+ ### 1.2. 表设计
4747
4848应该避免的设计问题:
4949
5252- ** 枚举** - 尽量不要用枚举,因为添加和删除字符串(枚举选项)必须使用 ` ALTER TABLE ` 。
5353- 尽量避免 ` NULL `
5454
55- ### 范式和反范式
55+ ### 1.3. 范式和反范式
5656
5757** 范式化目标是尽量减少冗余,而反范式化则相反** 。
5858
6868
6969在真实世界中,很少会极端地使用范式化或反范式化。实际上,应该权衡范式和反范式的利弊,混合使用。
7070
71- ### 索引优化
71+ ### 1.4. 索引优化
7272
7373> 索引优化应该是查询性能优化的最有效手段。
7474>
9797- ** 覆盖索引**
9898- ** 自增字段作主键**
9999
100- ## 二、 SQL 优化
100+ ## 2. SQL 优化
101101
102- SQL 优化后,可以通过执行计划 (` EXPLAIN ` )来查看优化效果。
102+ 使用 ` EXPLAIN ` 命令查看当前 SQL 是否使用了索引, 优化后,再通过执行计划 (` EXPLAIN ` )来查看优化效果。
103103
104104SQL 优化基本思路:
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
177177Mysql 优化器会在内部处理的时候相互转化这两类查询。它们都** 可以使用索引来优化,这也是最有效的优化方法** 。
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
214214MySQL 总是通过创建并填充临时表的方式来执行 ` 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;
256256SELECT * 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
266266mysql> explain select * from user_info where id = 2 \G
@@ -280,30 +280,36 @@ possible_keys: PRIMARY
2802801 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;
316322SET 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