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

  • 558 views
Uploaded on

 

More in: Travel , Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
558
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
12
Comments
0
Likes
3

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. MySQL 高级优化之— 理解查询执行 上海二三四五网络科技股份有限公司 互联网研发中心 王德玖 2013-10-11 www.2345.com
  • 2. 议程 • • • • Mysql 实例数据库安装 Explain介绍 举例 问答 2 www.2345.com
  • 3. 实例数据库安装 • 本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
  • 4. Explain 输出介绍 • • • • • • • • • • id :表的序列号标识 select_type: SELECT 的类型 table: table 或者 alias 的名称 type :连接的查询类型(本次重点) possible_keys: 有哪些mysql可用的索引 keys : MySQL 执行时实际使用的索引 key_len: 使用到的索引长度 Ref:哪些字段被用来和 key配合从表中获取结果集 Rows:查询需要扫描的估计行数 extra: 其他的附加信息 4 www.2345.com
  • 5. Explain 输出 type • SELECT 查询的访问类型 • 各种方法: • system/const 好 • eq_ref • ref • ref_or_null • index_merge • range • index • ALL 坏 5 www.2345.com
  • 6. Explain实例 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: 1 select_type: SIMPLE table: rental type: range possible_keys: rental_date,idx_fk_inventory_id key: rental_date key_len: 8 查询Tpye ref: NULL rows: 1 range Extra: Using where eq_ref *************************** 2. row *************************** id: 1 select_type: SIMPLE table: inventory type: eq_ref possible_keys: PRIMARY key: PRIMARY key_len: 3 ref: sakila.rental.inventory_id rows: 1 Extra: 2 rows in set (0.00 sec) 6 类型: www.2345.com
  • 7. 查询类型 – 避免 “ALL” • Type这一列是explain 输出的最重要的信息 • 它告诉我们mysql选择怎么样访问策略来获 取特定的结果 – Type 为“ALL”意味着什么呢? – 这一提示表示mysql获取结果需要进行一次全 表扫描 – 下页为一个全表扫描的实例 ... www.2345.com
  • 8. 全表扫描type:ALL root@localhost: sakila 01:21:19>EXPLAIN SELECT * FROM rental G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: rental 这里,我们看到了一个全表扫 type: ALL 描的的执行计划。在这个场景 possible_keys: NULL 中,没有给定where子句,查 key: NULL key_len: NULL 询优化器没有使用索引来过滤 ref: NULL 行。因此,注意, SELECT * rows: 16451 FROM rental 和下面我们将要 Extra: 1 row in set (0.00 sec) 给出的 ‘ SELECT rental_date from rental ’ ,仅仅选择一列的 效率是不一样的 … www.2345.com
  • 9. 查询类型 – ’INDEX’ 扫描 • ‘INDEX’ 扫描并不是很好的查询类型 – 它提示在表的索引上进行了一次全索引扫描 – 当然它比全表扫描来说性能要好,但是依然需 要消耗大量资源 – 下页为一个全索引扫描的实例 ... www.2345.com
  • 10. 全索引扫描type:INDEX root@localhost: sakila 01:21:23>EXPLAIN SELECT rental_date FROM rental G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: rental type: index 这里,我们看到了一个全索引扫 possible_keys: NULL 描的执行计划。通过指定我们仅 key: rental_date key_len: 13 想要的列,其实这就是告诉 ref: NULL mysql查询优化器,如果查询的 rows: 16451 列( rental_date )上包含有索引, Extra: Using index 1 row in set (0.00 sec) 那么就不需要表上的其他列,通 过索引自身就可以提供我们需要 的数据 … www.2345.com
  • 11. 原来如此 ”SELECT * …” www.2345.com
  • 12. 范围扫描类型:”RANGE”扫描 范围扫描 “range” 类型特点 指定 WHERE (or ON) 作为范围范围过滤 使用 BETWEEN 操作符 使用 IN() 操作符 带有 >, >=, <, <= 操作符 MySQL 有很多优化范围扫描的操作符,它们 一般访问比较快速 • 但是必须保证在操作的列上有索引 • 下页为一个范围扫描的实例 ... • • • • • • www.2345.com
  • 13. 范围扫描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: SIMPLE 这里我们看到一个范围扫描的 table: rental type: range 查询计划。 BETWEEN 操作符 possible_keys: rental_date 表示我们想要的数据在rental key: rental_date date 的某一范围之间。注意 key_len: 8 ref: NULL possible_keys 列提示有一个索 rows: 2614 引在 rental_date上,可以 为 Extra: Using where 优化器提供范围扫描匹配,如 1 row in set (0.00 sec) 果没有这个索引,情况又将如 果何呢? www.2345.com
  • 14. 怎么样?回到了 ’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: 0 mysql> EXPLAIN SELECT * FROM rental -> WHERE rental_date BETWEEN '2006-01-01' AND '2006-07-01' G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: rental type: ALL 因为rental_date 列没 possible_keys: NULL 有了索引,查询优化 key: NULL key_len: NULL 器不再使用范围扫描, ref: NULL 而回到了全表扫描, rows: 16462 可见恰当的索引是多 Extra: Using where 1 row in set (0.01 sec) 么重要 www.2345.com
  • 15. 合并索引类型 - ”index_merge” • 合并索引扫描 “index_merge” 类型特点 – 这一特性在mysql5.0以上版本才有 – 它的优势是在一张表上可以同时使用多个索引 • 在mysql5.0之前一张表上的查询近能使用一个索引 – 在or 类型的操作中很有帮助 – 下页看一个index_merge实例 ... www.2345.com
  • 16. 合并索引扫描 :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: SIMPLE table: rental type: index_merge possible_keys: PRIMARY,rental_date key: rental_date,PRIMARY key_len: 8,4 ref: NULL rows: 4 Extra: Using sort_union(rental_date,PRIMARY); Using where 1 row in set (0.02 sec) 这里使用了和并 索引扫描,优化 器使用到了连个 索引,在 mysql5.0之前之 能使用一个索引 www.2345.com
  • 17. 合并索引index_mereg 续… • 它可以在用于 – sort_union • 如前例子,或者是条件使用范围的非主键的列 – Union • OR 条件使用常量或范围条件是主键列并且是innodb表 – Intersection • AND 条件使用常量或范围条件是主键列且是innodb表 • 不能在全文索引上使用 • 可以使用下面的参数来关闭之 – optimizer_switch = index_merge=on,index_merge_union=on,index_merge_sort_u nion=on,index_merge_intersection=on www.2345.com
  • 18. Ref类型扫描 • 根据键值返回的结果集不大的情况 • 在索引列上有一个常量的时候(见下例) • 通过连接表的索引来访问另一个连接表的 时候(见后例) • 通过索引搜索或者多连接单独扫描时 www.2345.com
  • 19. 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: SIMPLE table: rental type: ref 这里有两个可选索引供优化 possible_keys: PRIMARY,rental_date 器过滤记录选用,但是优化 key: rental_date key_len: 8 器选用rental_date,先从此 ref: const 索引中搜索出符合条件的记 rows: 1 录,根据索引再找出基于in() Extra: Using where 1 row in set (0.00 sec) 表达式中相应记录过滤出结 果集 www.2345.com
  • 20. 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: SIMPLE table: inventory type: ALL possible_keys: PRIMARY key: NULL key_len: NULL ref: NULL rows: 4645 Extra: *************************** 2. row *************************** id: 1 select_type: SIMPLE table: rental type: ref possible_keys: idx_fk_inventory_id key: idx_fk_inventory_id key_len: 3 ref: sakila.inventory.inventory_id rows: 1 Extra: 2 rows in set (0.00 sec) 这是一个ref扫描的的实例, 它通过外表(inventory)连 接到(rental)表索引列来 查询。值得注意的是,虽然 我们把rental 表作为第一个 from 子句,但是执行计划依 然挑选inventory作为第一张 表进入,这是为什么呢?因 为行数少 … www.2345.com
  • 21. eq_ref类型扫描 • 从一表中读出一行记录以和前一个表中读 取出来的记录做联合 • Where条件用于做连接的列是一个primary key 或 unique 类型 • 直接看实例 … www.2345.com
  • 22. 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: SIMPLE table: c type: ALL possible_keys: PRIMARY key: NULL key_len: NULL ref: NULL rows: 16 Extra: *************************** 2. row *************************** select_type: SIMPLE table: fc type: ref possible_keys: PRIMARY,fk_film_category_category key: fk_film_category_category key_len: 1 ref: sakila.c.category_id rows: 1 Extra: Using index *************************** 3. row *************************** select_type: SIMPLE table: f type: eq_ref possible_keys: PRIMARY,idx_title key: PRIMARY key_len: 2 ref: sakila.fc.film_id rows: 1 Extra: Using where 我们注意到,第三行显示使用 了eq_ref访问,即使在作为 where条件的„film.title „有一可 用索引,但是优化器选择使用 primary key,这是为什么呢? 其实我们可以看到在表 category中有16行,通过 film_category表的链接列能够 快速获取数据,16列比通过 tittle索引过滤得到的结果集46 行要少。 www.2345.com
  • 23. 一个重要的分割索引列 mysql> EXPLAIN SELECT * FROM film WHERE title LIKE 'Tr%'G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: film 得到一个不错的范围查询,tittle上的 type: range 索引得到使用 possible_keys: idx_title key: idx_title key_len: 767 ref: NULL rows: 15 Extra: Using where mysql> EXPLAIN SELECT * FROM film WHERE LEFT(title,2) = 'Tr' G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: film 情况就不乐观了,索引没有得以使用, type: ALL 导致全表扫描为什么呢? possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 951 Extra: Using where www.2345.com
  • 24. index_subquery 和 unique_subquery • 它们出现在SELECT 的子查询中 • 子查询出现唯一结果集的时候为 unique_subquery,否则是index_subquery – unique_subquery的性能稍好,因为优化器可以 把子查询替换成常量集 – 因此,它变成了一个范围条件 • 通常,连接会有更好的性能 • 下面将展示它们的具体情况 www.2345.com
  • 25. 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: 1 select_type: PRIMARY table: r type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 15646 Extra: Using where *************************** 2. row *************************** id: 2 select_type: DEPENDENT SUBQUERY table: customer type: unique_subquery possible_keys: PRIMARY,idx_last_name key: PRIMARY key_len: 2 ref: func rows: 1 Extra: Using index; Using where 外表(rental)通过一个全表扫描, 并通过customer_id 常量集,where过 滤后,把此结果集应用到子查询中的 select,逐个进行自查询。注意,这里 的查询类型提示为依赖子查询 (DEPENDENT SUBQUERY),从技 术上说,这是不正确的,因为这里的 子查询不是相关子查询,而应该返回 一个常量列表。因此,这个查询的 “customer.last_name”上的索引没有仸 何价值 … www.2345.com
  • 26. 重写子查询为标准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: 1 select_type: SIMPLE table: c type: range possible_keys: PRIMARY,idx_last_name key: idx_last_name key_len: 137 ref: NULL rows: 54 Extra: Using where *************************** 2. row *************************** id: 1 select_type: SIMPLE table: r type: ref possible_keys: idx_fk_customer_id key: idx_fk_customer_id key_len: 2 ref: sakila.c.customer_id rows: 13 Extra: 优化器为标准连接给 了一个更好的执行计 划,并且有相同的查 询结果。这一方法值 得推荐-把相关子查询 重写为标准连接查询 www.2345.com
  • 27. 下讲引题 mysql> EXPLAIN SELECT r.staff_id, COUNT(*) -> FROM rental r GROUP BY r.staff_id G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: r type: index possible_keys: NULL key: idx_fk_staff_id key_len: 1 ref: NULL rows: 15646 Extra: Using index mysql> EXPLAIN SELECT r.return_date, COUNT(*) -> FROM rental r GROUP BY r.return_date G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: r type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 15646 Extra: Using temporary; Using filesort 我们注意到 Extra:的信息 完全不一样,这 是为什么呢? … www.2345.com
  • 28. 总结 • • • • 上面我们看了一系列explain输出的type 这是mysql执行计划里非常总要的信息 还有其他列的含义如何 尤其Extra: , key: www.2345.com
  • 29. 结束 Q&A 29 www.2345.com
  • 30. 谢谢 30 www.2345.com