Skip to content

Commit 72bbf71

Browse files
committed
优化格式
1 parent 86b227c commit 72bbf71

11 files changed

+252
-252
lines changed

explain-in-detail.adoc

+77-77
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
[#explain-in-detail]
2-
== MySQL `explain` 详解
2+
= MySQL `explain` 详解
33
include::_attributes.adoc[]
44

55
进行 MySQL 查询优化,`explain` 是必备技能。这章就来重点介绍一下 `explain`
66

77
image::assets/images/sql-joins.png[title="SQL Joins", alt="SQL Joins", width="95%"]
88

9-
=== 示例数据库
9+
== 示例数据库
1010

1111
为了方便后续讲解,这里使用 MySQL 官方提供的示例数据库: https://dev.mysql.com/doc/sakila/en/[MySQL : Sakila Sample Database^]。需求的小伙伴,请到官方页面下载并安装。
1212

@@ -21,7 +21,7 @@ image::assets/images/sakila-schema-detail-2.png[title="Sakila Sample Database",
2121

2222

2323
[[explain-syntax]]
24-
=== `EXPLAIN` 语法
24+
== `EXPLAIN` 语法
2525

2626
`DESCRIBE` 和 `EXPLAIN` 是同义词。在实践中,`DESCRIBE` 多用于显示表结构,而 `EXPLAIN` 多用于显示 SQL 语句的执行计划。
2727

@@ -64,7 +64,7 @@ FROM actor
6464
WHERE actor_id = 1;
6565
----
6666

67-
=== `DESCRIBE` 获取表结构
67+
== `DESCRIBE` 获取表结构
6868

6969
`DESCRIBE` 是 `SHOW COLUMNS` 的简写形式。
7070

@@ -81,7 +81,7 @@ mysql> DESCRIBE actor;
8181
+-------------+-------------------+------+-----+-------------------+-----------------------------------------------+
8282
----
8383

84-
=== `SHOW PROFILES` 显示执行时间
84+
== `SHOW PROFILES` 显示执行时间
8585

8686
在 MySQL 数据库中,可以通过配置 `profiling` 参数来启用 SQL 剖析。
8787

@@ -124,54 +124,54 @@ SHOW PROFILE SWAPS FOR QUERY 119;
124124

125125

126126
[[explain-output]]
127-
=== `EXPLAIN` 输出
127+
== `EXPLAIN` 输出
128128

129-
==== `id`
129+
=== `id`
130130

131131
`SELECT` 标识符,SQL 执行的顺序的标识,SQL 从大到小的执行。如果在语句中没子查询或关联查询,只有唯一的 `SELECT`,每行都将显示 `1`。否则,内层的 `SELECT` 语句一般会顺序编号,对应于其在原始语句中的位置
132132

133133
* `id` 相同时,执行顺序由上至下
134134
* 如果是子查询,`id` 的序号会递增,`id` 值越大优先级越高,越先被执行
135135
* 如果 `id` 相同,则认为是一组,从上往下顺序执行;在所有组中,`id` 值越大,优先级越高,越先执行
136136

137-
==== `select_type`
137+
=== `select_type`
138138

139139

140-
===== `SIMPLE`
140+
==== `SIMPLE`
141141
简单 `SELECT`,不使用 `UNION` 或子查询等
142142

143-
===== `PRIMARY`
143+
==== `PRIMARY`
144144
查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY
145145

146-
===== `UNION`
146+
==== `UNION`
147147
UNION中的第二个或后面的SELECT语句
148148

149-
===== `DEPENDENT UNION`
149+
==== `DEPENDENT UNION`
150150
UNION中的第二个或后面的SELECT语句,取决于外面的查询
151151

152-
===== `UNION RESULT`
152+
==== `UNION RESULT`
153153
UNION的结果
154154

155-
===== `SUBQUERY`
155+
==== `SUBQUERY`
156156
子查询中的第一个SELECT
157157

158-
===== `DEPENDENT SUBQUERY`
158+
==== `DEPENDENT SUBQUERY`
159159
子查询中的第一个SELECT,取决于外面的查询
160160

161-
===== `DERIVED`
161+
==== `DERIVED`
162162
派生表的SELECT, FROM子句的子查询
163163

164-
===== `DEPENDENT DERIVED`
164+
==== `DEPENDENT DERIVED`
165165
派生表的SELECT, FROM子句的子查询
166166
`MATERIALIZED`::
167167

168-
===== `UNCACHEABLE SUBQUERY`
168+
==== `UNCACHEABLE SUBQUERY`
169169
一个子查询的结果不能被缓存,必须重新评估外链接的第一行
170170

171-
===== `UNCACHEABLE UNION`
171+
==== `UNCACHEABLE UNION`
172172
??
173173

174-
==== `table`
174+
=== `table`
175175

176176
访问引用哪个表(例如下面的 `actor`):
177177

@@ -183,17 +183,17 @@ FROM actor
183183
WHERE actor_id = 1;
184184
----
185185

186-
==== `partitions`
186+
=== `partitions`
187187

188-
==== `type`
188+
=== `type`
189189

190190
`type` 显示的是数据访问类型,是较为重要的一个指标,结果值从好到坏依次是:
191191
`system` > `const` > `eq_ref` > `ref` > `fulltext` > `ref_or_null` > `index_merge` > `unique_subquery` > `index_subquery` > `range` > `index` > `ALL`。一般来说,得保证查询至少达到 `range` 级别,最好能达到 `ref`。
192192

193-
===== `system`
193+
==== `system`
194194
当 MySQL 对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于 `WHERE` 列表中,MySQL 就能将该查询转换为一个常量。`system` 是 `const` 类型的特例,当查询的表只有一行的情况下,使用 `system`。
195195

196-
===== `const`
196+
==== `const`
197197

198198
在查询开始时读取,该表最多有一个匹配行。因为只有一行,所以这一行中的列的值可以被其他优化器视为常量。`const` 表非常快,因为它们只读取一次。
199199

@@ -205,14 +205,14 @@ FROM actor
205205
WHERE actor_id = 1;
206206
----
207207

208-
===== `eq_ref`
208+
==== `eq_ref`
209209

210210
类似 `ref`,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用 `PRIMARY KEY` 或者 `UNIQUE KEY` 作为关联条件
211211

212212
最多只返回一条符合条件的记录。使用唯一性索引或主键查找时会发生(高效)。
213213

214214

215-
===== `ref`
215+
==== `ref`
216216

217217
表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
218218

@@ -226,7 +226,7 @@ FROM address
226226
WHERE city_id = 119;
227227
----
228228

229-
===== `fulltext`
229+
==== `fulltext`
230230

231231
全文检索
232232

@@ -239,17 +239,17 @@ WHERE MATCH(title, description) AGAINST('ACE')
239239
LIMIT 100;
240240
----
241241

242-
===== `ref_or_null`
242+
==== `ref_or_null`
243243

244244
MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。
245245

246-
===== `index_merge`
246+
==== `index_merge`
247247

248-
===== `unique_subquery`
248+
==== `unique_subquery`
249249

250-
===== `index_subquery`
250+
==== `index_subquery`
251251

252-
===== `range`
252+
==== `range`
253253

254254
范围扫描,一个有限制的索引扫描。`key` 列显示使用了哪个索引。当使用 `=`、 `<>`、`>`、`>=`、`<`、`<=`、`IS NULL`、`<=>`、`BETWEEN` 或者 `IN` 操作符,用常量比较关键字列时,可以使用 `range`。
255255

@@ -261,11 +261,11 @@ FROM actor
261261
WHERE actor_id > 100;
262262
----
263263

264-
===== `index`
264+
==== `index`
265265

266266
Full Index Scan,`index` 与 `ALL` 区别为 `index` 类型只遍历索引树。和全表扫描一样。只是扫描表的时候按照索引次序进行而不是行。主要优点就是避免了排序, 但是开销仍然非常大。如在 `Extra` 列看到 `Using index`,说明正在使用覆盖索引,只扫描索引的数据,它比按索引次序全表扫描的开销要小很多
267267

268-
===== `ALL`
268+
==== `ALL`
269269

270270
Full Table Scan,最坏的情况,全表扫描,MySQL 将遍历全表以找到匹配的行。
271271

@@ -276,51 +276,51 @@ SELECT *
276276
FROM actor;
277277
----
278278

279-
==== `possible_keys`
279+
=== `possible_keys`
280280

281281
显示查询使用了哪些索引,表示该索引可以进行高效地查找,但是列出来的索引对于后续优化过程可能是没有用的。
282282

283-
==== `key`
283+
=== `key`
284284

285285
`key` 列显示 MySQL 实际决定使用的键(索引)。如果没有选择索引,键是 `NULL`。要想强制 MySQL 使用或忽视 `possible_keys` 列中的索引,在查询中使用 `FORCE INDEX`、`USE INDEX` 或者 `IGNORE INDEX`。
286286

287-
==== `key_len`
287+
=== `key_len`
288288

289289
`key_len` 列显示 MySQL 决定使用的键长度。如果键是 `NULL`,则长度为 `NULL`。使用的索引的长度。在不损失精确性的情况下,长度越短越好 。
290290

291-
==== `ref`
291+
=== `ref`
292292

293293
`ref` 列显示使用哪个列或常数与 `key` 一起从表中选择行。
294294

295-
==== `rows`
295+
=== `rows`
296296

297297
`rows` 列显示 MySQL 认为它执行查询时必须检查的行数。注意这是一个预估值。
298298

299-
==== `filtered`
299+
=== `filtered`
300300

301301
给出了一个百分比的值,这个百分比值和 rows 列的值一起使用。(5.7才有)
302302

303-
==== `Extra`
303+
=== `Extra`
304304

305305
`Extra` 是 `EXPLAIN` 输出中另外一个很重要的列,该列显示 MySQL 在查询过程中的一些详细信息,MySQL 查询优化器执行查询的过程中对查询计划的重要补充信息。
306306

307-
===== `Child of *'table'* pushed join@1`
307+
==== `Child of *'table'* pushed join@1`
308308

309-
===== `const row not found`
309+
==== `const row not found`
310310

311-
===== `Deleting all rows`
311+
==== `Deleting all rows`
312312

313-
===== `Distinct`
313+
==== `Distinct`
314314

315315
优化 `DISTINCT` 操作,在找到第一匹配的元组后即停止找同样值的动作
316316

317-
===== `FirstMatch(**tbl_name**)`
317+
==== `FirstMatch(**tbl_name**)`
318318

319-
===== `Full scan on NULL key`
319+
==== `Full scan on NULL key`
320320

321-
===== `Impossible HAVING`
321+
==== `Impossible HAVING`
322322

323-
===== `Impossible WHERE`
323+
==== `Impossible WHERE`
324324

325325
[{sql_source_attr}]
326326
----
@@ -333,28 +333,28 @@ WHERE actor_id IS NULL;
333333
因为 `actor_id` 是 `actor` 表的主键。所以,这个条件不可能成立。
334334

335335

336-
===== `Impossible WHERE noticed after reading const tables`
336+
==== `Impossible WHERE noticed after reading const tables`
337337

338-
===== `LooseScan(**m..n**)`
338+
==== `LooseScan(**m..n**)`
339339

340-
===== `No matching min/max row`
340+
==== `No matching min/max row`
341341

342-
===== `no matching row in const table`
342+
==== `no matching row in const table`
343343

344-
===== `No matching rows after partition pruning`
344+
==== `No matching rows after partition pruning`
345345

346-
===== `No tables used`
346+
==== `No tables used`
347347

348-
===== `Not exists`
348+
==== `Not exists`
349349

350350
MySQL 优化了 `LEFT JOIN`,一旦它找到了匹配 `LEFT JOIN` 标准的行, 就不再搜索了。
351351

352-
===== `Plan isn't ready yet`
353-
===== `Range checked for each record (index map: **N**)`
354-
===== `Recursive`
355-
===== `Rematerialize`
356-
===== `Scanned *N* databases`
357-
===== `Select tables optimized away`
352+
==== `Plan isn't ready yet`
353+
==== `Range checked for each record (index map: **N**)`
354+
==== `Recursive`
355+
==== `Rematerialize`
356+
==== `Scanned *N* databases`
357+
==== `Select tables optimized away`
358358

359359
在没有 `GROUP BY` 子句的情况下,基于索引优化 `MIN/MAX` 操作,或者对于 MyISAM 存储引擎优化 `COUNT(*)` 操作,不必等到执行阶段再进行计算,查询执行计划生成的阶段即完成优化。
360360

@@ -365,39 +365,39 @@ SELECT MIN(actor_id), MAX(actor_id)
365365
FROM actor;
366366
----
367367

368-
===== `Skip_open_table, Open_frm_only, Open_full_table`
368+
==== `Skip_open_table, Open_frm_only, Open_full_table`
369369

370370
* `Skip_open_table`
371371
* `Open_frm_only`
372372
* `Open_full_table`
373373

374-
===== `Start temporary, End temporary`
375-
===== `unique row not found`
376-
===== `Using filesort`
374+
==== `Start temporary, End temporary`
375+
==== `unique row not found`
376+
==== `Using filesort`
377377

378378
MySQL 有两种方式可以生成有序的结果,通过排序操作或者使用索引,当 `Extra` 中出现了 `Using filesort` 说明MySQL使用了后者,但注意虽然叫 `filesort` 但并不是说明就是用了文件来进行排序,只要可能排序都是在内存里完成的。大部分情况下利用索引排序更快,所以一般这时也要考虑优化查询了。使用文件完成排序操作,这是可能是 `ordery by`,`group by` 语句的结果,这可能是一个 CPU 密集型的过程,可以通过选择合适的索引来改进性能,用索引来为查询结果排序。
379379

380-
===== `Using index`
380+
==== `Using index`
381381

382382
说明查询是覆盖了索引的,不需要读取数据文件,从索引树(索引文件)中即可获得信息。如果同时出现 `using where`,表明索引被用来执行索引键值的查找,没有 `using where`,表明索引用来读取数据而非执行查找动作。这是MySQL 服务层完成的,但无需再回表查询记录。
383383

384-
===== `Using index condition`
384+
==== `Using index condition`
385385

386386
这是 MySQL 5.6 出来的新特性,叫做“索引条件推送”。简单说一点就是 MySQL 原来在索引上是不能执行如 `like` 这样的操作的,但是现在可以了,这样减少了不必要的 I/O 操作,但是只能用在二级索引上。
387387

388-
===== `Using index for group-by`
389-
===== `Using index for skip scan`
390-
===== `Using join buffer (Block Nested Loop), Using join buffer (Batched Key Access)`
388+
==== `Using index for group-by`
389+
==== `Using index for skip scan`
390+
==== `Using join buffer (Block Nested Loop), Using join buffer (Batched Key Access)`
391391

392392
使用了连接缓存:Block Nested Loop,连接算法是块嵌套循环连接;Batched Key Access,连接算法是批量索引连接。
393393

394-
===== `Using MRR`
395-
===== `Using sort_union(...), Using union(...), Using intersect(...)`
396-
===== `Using temporary`
394+
==== `Using MRR`
395+
==== `Using sort_union(...), Using union(...), Using intersect(...)`
396+
==== `Using temporary`
397397

398398
用临时表保存中间结果,常用于 `GROUP BY` 和 `ORDER BY` 操作中,一般看到它说明查询需要优化了,就算避免不了临时表的使用也要尽量避免硬盘临时表的使用。
399399

400-
===== `Using where`
400+
==== `Using where`
401401

402402
使用了 `WHERE` 从句来限制哪些行将与下一张表匹配或者是返回给用户。注意:`Extra` 列出现 `Using where` 表示MySQL 服务器将存储引擎返回服务层以后再应用 `WHERE` 条件过滤。
403403

@@ -409,8 +409,8 @@ FROM actor
409409
WHERE actor_id > 100;
410410
----
411411

412-
===== `Using where with pushed condition`
413-
===== `Zero limit`
412+
==== `Using where with pushed condition`
413+
==== `Zero limit`
414414

415415
查询有 `LIMIT 0` 子句,所以导致不能选出任何一行。
416416

explain-in-practice.adoc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[#explain-in-practice]
2-
== `EXPLAIN` 实践
2+
= `EXPLAIN` 实践
33

44
如果 `EXPLAIN` 执行计划的 `Extra` 列包含 “Using temporary”,则说明这个查询使用了隐式临时表。
55

0 commit comments

Comments
 (0)