mysql 高级优化之 理解查询执行

30
www.2345.com MySQL 级优化之理解查询执行 上海二三四五网络科技股份有限公司 联网研发中心 王德玖 2013-10-11

Upload: nigel889

Post on 13-Jul-2015

676 views

Category:

Travel


3 download

TRANSCRIPT

Page 1: Mysql 高级优化之 理解查询执行

www.2345.com

MySQL 高级优化之—

理解查询执行

上海二三四五网络科技股份有限公司

互联网研发中心

王德玖

2013-10-11

Page 2: Mysql 高级优化之 理解查询执行

www.2345.com

议程

• Mysql 实例数据库安装

• Explain介绍

• 举例

• 问答

2

Page 3: Mysql 高级优化之 理解查询执行

www.2345.com

实例数据库安装

• 本ppt使用 sakila 数据库为事例;

• 下载地址:

http://downloads.mysql.com/docs/sakila-db.tar.gz

• 安装事例数据库:

• [root@locahost]# tar -xzvf sakila.sql.tar.gz

• mysql> source /opt/src/sakila/ sakila-schema.sql

• mysql> source /opt/src/sakila/ sakila-data.sql

• 内网ip:172.16.0.230 已经安装此测试库

3

Page 4: Mysql 高级优化之 理解查询执行

www.2345.com

Explain 输出介绍

• id :表的序列号标识

• select_type: SELECT 的类型

• table: table 或者 alias 的名称• type :连接的查询类型(本次重点)• possible_keys:有哪些mysql可用的索引• keys : MySQL 执行时实际使用的索引

• key_len:使用到的索引长度

• Ref:哪些字段被用来和 key配合从表中获取结果集

• Rows:查询需要扫描的估计行数

• extra:其他的附加信息

4

Page 5: Mysql 高级优化之 理解查询执行

www.2345.com

Explain 输出 type

• SELECT 查询的访问类型

• 各种方法:

• system/const 好• eq_ref

• ref

• ref_or_null

• index_merge

• range

• index

• ALL 坏

5

Page 6: Mysql 高级优化之 理解查询执行

www.2345.com

Explain实例

6

root@localhost: >EXPLAIN SELECT * FROM rental INNER JOIN inventory ON> rental.inventory_id = inventory.inventory_id WHERE rental_date BETWEEN '2006-03-01' AND '2006-08-01'\G *************************** 1. row ***************************

id: 1select_type: SIMPLE

table: rentaltype: range

possible_keys: rental_date,idx_fk_inventory_idkey: rental_date

key_len: 8ref: NULL

rows: 1Extra: Using where

*************************** 2. row ***************************id: 1

select_type: SIMPLEtable: inventorytype: eq_ref

possible_keys: PRIMARYkey: PRIMARY

key_len: 3ref: sakila.rental.inventory_id

rows: 1Extra:

2 rows in set (0.00 sec)

查询Tpye 类型:

rangeeq_ref

Page 7: Mysql 高级优化之 理解查询执行

www.2345.com

查询类型 – 避免 “ALL”

• Type这一列是explain 输出的最重要的信息

• 它告诉我们mysql选择怎么样访问策略来获

取特定的结果

– Type 为“ALL”意味着什么呢?

– 这一提示表示mysql获取结果需要进行一次全

表扫描

– 下页为一个全表扫描的实例 ...

Page 8: Mysql 高级优化之 理解查询执行

www.2345.com

全表扫描type:ALL

root@localhost: sakila 01:21:19>EXPLAIN SELECT * FROM rental \G*************************** 1. row ***************************

id: 1select_type: SIMPLE

table: rentaltype: ALL

possible_keys: NULLkey: NULL

key_len: NULLref: NULLrows: 16451

Extra: 1 row in set (0.00 sec)

这里,我们看到了一个全表扫

描的的执行计划。在这个场景

中,没有给定where子句,查

询优化器没有使用索引来过滤

行。因此,注意, SELECT * FROM rental 和下面我们将要

给出的 ‘ SELECT rental_datefrom rental ’ ,仅仅选择一列的

效率是不一样的 …

Page 9: Mysql 高级优化之 理解查询执行

www.2345.com

查询类型 – ’INDEX’ 扫描

• ‘INDEX’ 扫描并不是很好的查询类型

– 它提示在表的索引上进行了一次全索引扫描

– 当然它比全表扫描来说性能要好,但是依然需

要消耗大量资源

– 下页为一个全索引扫描的实例 ...

Page 10: Mysql 高级优化之 理解查询执行

www.2345.com

全索引扫描type:INDEX

root@localhost: sakila 01:21:23>EXPLAIN SELECT rental_date FROM rental \G*************************** 1. row ***************************

id: 1select_type: SIMPLE

table: rentaltype: index

possible_keys: NULLkey: rental_date

key_len: 13ref: NULLrows: 16451Extra: Using index

1 row in set (0.00 sec)

这里,我们看到了一个全索引扫

描的执行计划。通过指定我们仅

想要的列,其实这就是告诉

mysql查询优化器,如果查询的

列( rental_date )上包含有索引,那么就不需要表上的其他列,通

过索引自身就可以提供我们需要

的数据 …

Page 11: Mysql 高级优化之 理解查询执行

www.2345.com

原来如此 ”SELECT * …”

Page 12: Mysql 高级优化之 理解查询执行

www.2345.com

范围扫描类型:”RANGE”扫描

• 范围扫描 “range” 类型特点

• 指定 WHERE (or ON) 作为范围范围过滤

• 使用 BETWEEN 操作符

• 使用 IN() 操作符

• 带有 >, >=, <, <= 操作符

• MySQL 有很多优化范围扫描的操作符,它们一般访问比较快速

• 但是必须保证在操作的列上有索引

• 下页为一个范围扫描的实例 ...

Page 13: Mysql 高级优化之 理解查询执行

www.2345.com

范围扫描type:RANGE

root@localhost: sakila 02:53:19>EXPLAIN SELECT * FROM rental ->WHERE rental_date BETWEEN '2006-01-01' AND '2006-07-01'\G

*************************** 1. row ***************************id: 1

select_type: SIMPLEtable: rentaltype: range

possible_keys: rental_datekey: rental_date

key_len: 8ref: NULLrows: 2614

Extra: Using where1 row in set (0.00 sec)

这里我们看到一个范围扫描的

查询计划。 BETWEEN 操作符表示我们想要的数据在rental date 的某一范围之间。注意

possible_keys 列提示有一个索引在 rental_date上,可以 为

优化器提供范围扫描匹配,如

果没有这个索引,情况又将如

果何呢?

Page 14: Mysql 高级优化之 理解查询执行

www.2345.com

怎么样?回到了 ’ALL’

root@localhost: sakila 02:53:19> DROP INDEX rental_date ON rental;Query OK, 16044 rows affected (1.20 sec)Records: 16044 Duplicates: 0 Warnings: 0mysql> EXPLAIN SELECT * FROM rental-> WHERE rental_date BETWEEN '2006-01-01' AND '2006-07-01' \G*************************** 1. row ***************************id: 1select_type: SIMPLEtable: rentaltype: ALLpossible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: 16462Extra: Using where1 row in set (0.01 sec)

因为rental_date 列没有了索引,查询优化

器不再使用范围扫描,

而回到了全表扫描,

可见恰当的索引是多

么重要

Page 15: Mysql 高级优化之 理解查询执行

www.2345.com

合并索引类型 - ”index_merge”

• 合并索引扫描 “index_merge” 类型特点

– 这一特性在mysql5.0以上版本才有

– 它的优势是在一张表上可以同时使用多个索引

• 在mysql5.0之前一张表上的查询近能使用一个索引

– 在or 类型的操作中很有帮助

– 下页看一个index_merge实例 ...

Page 16: Mysql 高级优化之 理解查询执行

www.2345.com

合并索引扫描 :index_merge

root@localhost: sakila 03:27:44>EXPLAIN SELECT * FROM rental-> WHERE rental_id IN (10,11,12)-> OR rental_date = '2006-02-01' \G

*************************** 1. row ***************************id: 1

select_type: SIMPLEtable: rentaltype: index_merge

possible_keys: PRIMARY,rental_datekey: rental_date,PRIMARY

key_len: 8,4ref: NULL

rows: 4Extra: Using sort_union(rental_date,PRIMARY); Using where

1 row in set (0.02 sec)

这里使用了和并

索引扫描,优化

器使用到了连个

索引,在mysql5.0之前之能使用一个索引

Page 17: Mysql 高级优化之 理解查询执行

www.2345.com

合并索引index_mereg 续…

• 它可以在用于– sort_union

• 如前例子,或者是条件使用范围的非主键的列

– Union• OR 条件使用常量或范围条件是主键列并且是innodb表

– Intersection• AND 条件使用常量或范围条件是主键列且是innodb表

• 不能在全文索引上使用• 可以使用下面的参数来关闭之

– optimizer_switch = index_merge=on,index_merge_union=on,index_merge_sort_union=on,index_merge_intersection=on

Page 18: Mysql 高级优化之 理解查询执行

www.2345.com

Ref类型扫描

• 根据键值返回的结果集不大的情况

• 在索引列上有一个常量的时候(见下例)

• 通过连接表的索引来访问另一个连接表的

时候(见后例)

• 通过索引搜索或者多连接单独扫描时

Page 19: Mysql 高级优化之 理解查询执行

www.2345.com

Ref类型扫描实例( on indexed 列 )

root@localhost: sakila 04:40:44>EXPLAIN SELECT * FROM rental ->WHERE rental_id IN (10,11,12)-> AND rental_date = '2006-02-01'\G

*************************** 1. row ***************************id: 1

select_type: SIMPLEtable: rentaltype: ref

possible_keys: PRIMARY,rental_datekey: rental_date

key_len: 8ref: constrows: 1Extra: Using where

1 row in set (0.00 sec)

这里有两个可选索引供优化

器过滤记录选用,但是优化

器选用rental_date,先从此索引中搜索出符合条件的记

录,根据索引再找出基于in()表达式中相应记录过滤出结

果集

Page 20: Mysql 高级优化之 理解查询执行

www.2345.com

Ref类型扫描实例( on join )

root@localhost: : sakila >EXPLAIN SELECT * FROM rental -> INNER JOIN inventory ON inventory.inventory_id=rental.inventory_id \G

*************************** 1. row ***************************id: 1

select_type: SIMPLEtable: inventorytype: ALL

possible_keys: PRIMARYkey: NULL

key_len: NULLref: NULL

rows: 4645Extra:

*************************** 2. row ***************************id: 1

select_type: SIMPLEtable: rentaltype: ref

possible_keys: idx_fk_inventory_idkey: idx_fk_inventory_id

key_len: 3ref: sakila.inventory.inventory_id

rows: 1Extra:

2 rows in set (0.00 sec)

这是一个ref扫描的的实例,

它通过外表(inventory)连

接到(rental)表索引列来查询。值得注意的是,虽然

我们把rental 表作为第一个

from 子句,但是执行计划依

然挑选inventory作为第一张

表进入,这是为什么呢?因

为行数少 …

Page 21: Mysql 高级优化之 理解查询执行

www.2345.com

eq_ref类型扫描

• 从一表中读出一行记录以和前一个表中读

取出来的记录做联合

• Where条件用于做连接的列是一个primary key 或 unique 类型

• 直接看实例 …

Page 22: Mysql 高级优化之 理解查询执行

www.2345.com

eq_ref类型扫描实例

root@localhost: sakila 09:48:41> EXPLAIN SELECT f.film_id, f.title, c.name> FROM film f INNER JOIN film_category fc> ON f.film_id=fc.film_id INNER JOIN category c> ON fc.category_id=c.category_id WHERE f.title LIKE 'T%' \G

*************************** 1. row ***************************select_type: SIMPLEtable: ctype: ALLpossible_keys: PRIMARYkey: NULLkey_len: NULLref: NULLrows: 16Extra:*************************** 2. row ***************************select_type: SIMPLEtable: fctype: refpossible_keys: PRIMARY,fk_film_category_categorykey: fk_film_category_categorykey_len: 1ref: sakila.c.category_idrows: 1Extra: Using index*************************** 3. row ***************************select_type: SIMPLEtable: ftype: eq_refpossible_keys: PRIMARY,idx_titlekey: PRIMARYkey_len: 2ref: sakila.fc.film_idrows: 1Extra: Using where

我们注意到,第三行显示使用

了eq_ref访问,即使在作为

where条件的„film.title „有一可用索引,但是优化器选择使用

primary key,这是为什么呢?

其实我们可以看到在表

category中有16行,通过

film_category表的链接列能够

快速获取数据,16列比通过

tittle索引过滤得到的结果集46行要少。

Page 23: Mysql 高级优化之 理解查询执行

www.2345.com

一个重要的分割索引列

mysql> EXPLAIN SELECT * FROM film WHERE title LIKE 'Tr%'\G*************************** 1. row ***************************id: 1select_type: SIMPLEtable: filmtype: rangepossible_keys: idx_titlekey: idx_titlekey_len: 767ref: NULLrows: 15Extra: Using wheremysql> EXPLAIN SELECT * FROM film WHERE LEFT(title,2) = 'Tr' \G*************************** 1. row ***************************id: 1select_type: SIMPLEtable: filmtype: ALLpossible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: 951Extra: Using where

得到一个不错的范围查询,tittle上的索引得到使用

情况就不乐观了,索引没有得以使用,

导致全表扫描为什么呢?

Page 24: Mysql 高级优化之 理解查询执行

www.2345.com

index_subquery 和 unique_subquery

• 它们出现在SELECT 的子查询中

• 子查询出现唯一结果集的时候为

unique_subquery,否则是index_subquery

– unique_subquery的性能稍好,因为优化器可以

把子查询替换成常量集

– 因此,它变成了一个范围条件

• 通常,连接会有更好的性能

• 下面将展示它们的具体情况

Page 25: Mysql 高级优化之 理解查询执行

www.2345.com

unique_subquery访问实例

root@localhost: sakila > EXPLAIN SELECT * FROM rental r WHERE r.customer_id IN (-> SELECT customer_id FROM customer WHERE last_name LIKE 'S%') \G

*************************** 1. row ***************************id: 1select_type: PRIMARYtable: rtype: ALLpossible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: 15646Extra: Using where*************************** 2. row ***************************id: 2select_type: DEPENDENT SUBQUERYtable: customertype: unique_subquerypossible_keys: PRIMARY,idx_last_namekey: PRIMARYkey_len: 2ref: funcrows: 1Extra: Using index; Using where

外表(rental)通过一个全表扫描,

并通过customer_id 常量集,where过

滤后,把此结果集应用到子查询中的

select,逐个进行自查询。注意,这里

的查询类型提示为依赖子查询

(DEPENDENT SUBQUERY),从技术上说,这是不正确的,因为这里的

子查询不是相关子查询,而应该返回

一个常量列表。因此,这个查询的

“customer.last_name”上的索引没有仸何价值 …

Page 26: Mysql 高级优化之 理解查询执行

www.2345.com

重写子查询为标准join

root@localhost: sakila> EXPLAIN SELECT * FROM rental r-> INNER JOIN customer c ON r.customer_id=c.customer_id WHERE last_name LIKE 'S%' \G

*************************** 1. row ***************************id: 1select_type: SIMPLEtable: ctype: rangepossible_keys: PRIMARY,idx_last_namekey: idx_last_namekey_len: 137ref: NULLrows: 54Extra: Using where*************************** 2. row ***************************id: 1select_type: SIMPLEtable: rtype: refpossible_keys: idx_fk_customer_idkey: idx_fk_customer_idkey_len: 2ref: sakila.c.customer_idrows: 13Extra:

优化器为标准连接给

了一个更好的执行计

划,并且有相同的查

询结果。这一方法值

得推荐-把相关子查询

重写为标准连接查询

Page 27: Mysql 高级优化之 理解查询执行

www.2345.com

下讲引题

mysql> EXPLAIN SELECT r.staff_id, COUNT(*)-> FROM rental r GROUP BY r.staff_id \G*************************** 1. row ***************************id: 1select_type: SIMPLEtable: rtype: indexpossible_keys: NULLkey: idx_fk_staff_idkey_len: 1ref: NULLrows: 15646Extra: Using indexmysql> EXPLAIN SELECT r.return_date, COUNT(*)-> FROM rental r GROUP BY r.return_date \G*************************** 1. row ***************************id: 1select_type: SIMPLEtable: rtype: ALLpossible_keys: NULLkey: NULLkey_len: NULLref: NULLrows: 15646Extra: Using temporary; Using filesort

我们注意到Extra:的信息完全不一样,这

是为什么呢? …

Page 28: Mysql 高级优化之 理解查询执行

www.2345.com

总结

• 上面我们看了一系列explain输出的type

• 这是mysql执行计划里非常总要的信息

• 还有其他列的含义如何

• 尤其Extra: , key:

Page 29: Mysql 高级优化之 理解查询执行

www.2345.com

结束

Q&A

29

Page 30: Mysql 高级优化之 理解查询执行

www.2345.com

谢谢

30