Upload
nigel889
View
676
Download
3
Embed Size (px)
Citation preview
www.2345.com
MySQL 高级优化之—
理解查询执行
上海二三四五网络科技股份有限公司
互联网研发中心
王德玖
2013-10-11
www.2345.com
议程
• Mysql 实例数据库安装
• Explain介绍
• 举例
• 问答
2
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
www.2345.com
Explain 输出介绍
• id :表的序列号标识
• select_type: SELECT 的类型
• table: table 或者 alias 的名称• type :连接的查询类型(本次重点)• possible_keys:有哪些mysql可用的索引• keys : MySQL 执行时实际使用的索引
• key_len:使用到的索引长度
• Ref:哪些字段被用来和 key配合从表中获取结果集
• Rows:查询需要扫描的估计行数
• extra:其他的附加信息
4
www.2345.com
Explain 输出 type
• SELECT 查询的访问类型
• 各种方法:
• system/const 好• eq_ref
• ref
• ref_or_null
• index_merge
• range
• index
• ALL 坏
5
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
www.2345.com
查询类型 – 避免 “ALL”
• Type这一列是explain 输出的最重要的信息
• 它告诉我们mysql选择怎么样访问策略来获
取特定的结果
– Type 为“ALL”意味着什么呢?
– 这一提示表示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 ’ ,仅仅选择一列的
效率是不一样的 …
www.2345.com
查询类型 – ’INDEX’ 扫描
• ‘INDEX’ 扫描并不是很好的查询类型
– 它提示在表的索引上进行了一次全索引扫描
– 当然它比全表扫描来说性能要好,但是依然需
要消耗大量资源
– 下页为一个全索引扫描的实例 ...
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 )上包含有索引,那么就不需要表上的其他列,通
过索引自身就可以提供我们需要
的数据 …
www.2345.com
原来如此 ”SELECT * …”
www.2345.com
范围扫描类型:”RANGE”扫描
• 范围扫描 “range” 类型特点
• 指定 WHERE (or ON) 作为范围范围过滤
• 使用 BETWEEN 操作符
• 使用 IN() 操作符
• 带有 >, >=, <, <= 操作符
• 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上,可以 为
优化器提供范围扫描匹配,如
果没有这个索引,情况又将如
果何呢?
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 列没有了索引,查询优化
器不再使用范围扫描,
而回到了全表扫描,
可见恰当的索引是多
么重要
www.2345.com
合并索引类型 - ”index_merge”
• 合并索引扫描 “index_merge” 类型特点
– 这一特性在mysql5.0以上版本才有
– 它的优势是在一张表上可以同时使用多个索引
• 在mysql5.0之前一张表上的查询近能使用一个索引
– 在or 类型的操作中很有帮助
– 下页看一个index_merge实例 ...
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之前之能使用一个索引
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
www.2345.com
Ref类型扫描
• 根据键值返回的结果集不大的情况
• 在索引列上有一个常量的时候(见下例)
• 通过连接表的索引来访问另一个连接表的
时候(见后例)
• 通过索引搜索或者多连接单独扫描时
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()表达式中相应记录过滤出结
果集
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作为第一张
表进入,这是为什么呢?因
为行数少 …
www.2345.com
eq_ref类型扫描
• 从一表中读出一行记录以和前一个表中读
取出来的记录做联合
• Where条件用于做连接的列是一个primary key 或 unique 类型
• 直接看实例 …
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行要少。
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上的索引得到使用
情况就不乐观了,索引没有得以使用,
导致全表扫描为什么呢?
www.2345.com
index_subquery 和 unique_subquery
• 它们出现在SELECT 的子查询中
• 子查询出现唯一结果集的时候为
unique_subquery,否则是index_subquery
– unique_subquery的性能稍好,因为优化器可以
把子查询替换成常量集
– 因此,它变成了一个范围条件
• 通常,连接会有更好的性能
• 下面将展示它们的具体情况
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”上的索引没有仸何价值 …
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:
优化器为标准连接给
了一个更好的执行计
划,并且有相同的查
询结果。这一方法值
得推荐-把相关子查询
重写为标准连接查询
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:的信息完全不一样,这
是为什么呢? …
www.2345.com
总结
• 上面我们看了一系列explain输出的type
• 这是mysql执行计划里非常总要的信息
• 还有其他列的含义如何
• 尤其Extra: , key:
www.2345.com
结束
Q&A
29
www.2345.com
谢谢
30