`
hudeyong926
  • 浏览: 2017333 次
  • 来自: 武汉
社区版块
存档分类
最新评论

EXPLAIN sql优化方法(1) 添加索引

阅读更多
添加索引优化器更高效率地执行语句 

假设我们有两个数据表t1和t2,每个有1000行,包含的值从1到1000。下面的查询查找出两个表中值相同的数据行:

mysql> SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2;
+------+------+
| i1 | i2 |
+------+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |

两个表都没有索引的时候,EXPLAIN产生下面的结果:

mysql> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1000
Extra:
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t2
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1000
Extra: Using whe

 类型列中的ALL表明要进行检查所有数据行的全表扫描。可能键列中的NULL表明没有找到用于提高查询速度的备选索引(键、键长度和参考列都是NULL也是因为缺少合适的索引)。Using where表明使用WHERE子句中的信息来识别合格的数据行。 这段信息告诉我们,优化器没有为提高执行查询的效率找到任何有用的信息:
它将对t1表进行全表扫描。

对于t1中的每一行,它将执行t2的全表扫描,使用WHERE子句中的信息识别出合格的行。
行数值显示了优化器估计的每个阶段查询需要检查的行数。T1的估计值是1000,因为1000可以完成全表扫描。相似地,t2的估计值也是1000,但是这个值是对于t1的每一行的。换句话说,优化器所估计的处理该查询所需要检查的数据行组合的数量是1000×1000,也就是一百万。这会造成很大的浪费 ,因为实际上只有1000个组合符合WHERE子句的条件。

 

为了使这个查询的效率更高,给其中一个联结列添加索引 并重新执行EXPLAIN语句:

mysql> ALTER TABLE t2 ADD INDEX (i2);
mysql> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1000
Extra:
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t2
type: ref
possible_keys: i2
key: i2
key_len: 5
ref: sampdb.t1.i1
rows: 10
Extra: Using where; Using index

 我们可以看到性能提高了。T1的输出没有改变(表明还是需要进行全表扫描),但是优化器处理t2的方式就有所不同了:
类型从ALL改变为ref,意味着可以使用参考值(来自t1的值)来执行索引查找,定位t2中合格的数据行。
参考值在参考(ref)字段中给出了:sampdb.t1.i1。
数值从1000降低到了10,显示出优化器相信对于t1中的每一行,它只需要检查t2中的10行(这是一个悲观的估计值。实际上,在t2中只有一行与 t1中数据行匹配。我们在后面会看到如何帮助优化器改善这个估计值)。数据行组合的全部估计值使1000×10=10000。它比前面的没有索引的时候估 计出来的一百万好多了。
对t1进行索引有价值吗?实际上,对于这个特定的联结操作,扫描一张表是必要的,因此没有必要对t1建立索引。如果你想看到效果,可以索引t1.i1并再次运行EXPLAIN:

mysql> ALTER TABLE t1 ADD INDEX (i1);
mysql> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: index
possible_keys: i1
key: i1
key_len: 5
ref: NULL
rows: 1000
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t2
type: ref
possible_keys: i2
key: i2
key_len: 5
ref: sampdb.t1.i1
rows: 10
Extra: Using where; Using index

上面的输出与前面的EXPLAIN的输出相似,但是添加索引对t1的输出有一些改变。类型从NULL改成了index,附加(Extra)从空的改成了 Using index。这些改变表明,尽管对索引的值仍然需要执行全表扫描,但是优化器还是可以直接从索引文件中读取值,根据不需要使用数据文件。你可以从 MyISAM表中看到这类结果,在这种情况下,优化器知道自己只询问索引文件就能够得到所有需要的信息。对于InnoDB 和BDB表也有这样的结果,在这种情况下优化器可以单独使用索引中的信息而不用搜索数据行。

我们可以运行ANALYZE TABLE使优化器进一步优化估计值。这会引起服务器生成键值的静态分布。分析上面的表并再次运行EXPLAIN得到了更好的估计值:

mysql> ANALYZE TABLE t1, t2;
mysql> EXPLAIN SELECT t1.i1, t2.i2 FROM t1, t2 WHERE t1.i1 = t2.i2\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: t1
type: index
possible_keys: i1
key: i1
key_len: 5
ref: NULL
rows: 1000
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: t2
type: ref
possible_keys: i2
key: i2
key_len: 5
ref: sampdb.t1.i1
rows: 1
Extra: Using where; Using inde

在这种情况下,优化器估计在t2中与t1的每个值匹配的数据行只有一个。

 

mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B where A . catid = B . id ;
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
| id | select_type | table | type    | possible_keys | key      | key_len | ref                  | rows   | Extra        |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
|  1 | SIMPLE       | A      | ALL     | NULL           | NULL     | NULL     | NULL                 | 46585 |             |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY        | PRIMARY | 4        | joomla_test . A . catid |      1 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
2 rows in set ( 0.00 sec )

这个是我们经常使用的一种查询方式,对B表的联接类型使用了eq_ref,索引使用了PRIMARY,但是对于A表,却没有使用任何索引,这可能不是我们想要的。
查看以上SQL语句,我们可能会想到,有必要给A.catid加个索引了。

mysql > alter table jos_content add index idx_catid ( ` catid ` ) ;
Query OK , 46585 rows affected ( 0.75 sec )
Records : 46585   Duplicates : 0   Warnings : 0
 
mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B where A . catid = B . id ;
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
| id | select_type | table | type    | possible_keys | key      | key_len | ref                  | rows   | Extra        |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
|  1 | SIMPLE       | A      | ALL     | idx_catid      | NULL     | NULL     | NULL                 | 46585 |             |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY        | PRIMARY | 4        | joomla_test . A . catid |      1 | Using where |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+-------------+
2 rows in set ( 0.00 sec )
这样表A便使用了idx_catid索引。下面我们做一次三个表的联合查询
mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B , jos_sections C where A . catid = B . id and A . sectionid = C . id ;
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+--------------------------------+
| id | select_type | table | type    | possible_keys | key      | key_len | ref                  | rows   | Extra                           |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+--------------------------------+
|  1 | SIMPLE       | C      | index   | PRIMARY        | PRIMARY | 4        | NULL                 |      2 | Using index                     |
|  1 | SIMPLE       | A      | ALL     | idx_catid      | NULL     | NULL     | NULL                 | 46585 | Using where ; Using join buffer |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY        | PRIMARY | 4        | joomla_test . A . catid |      1 | Using where                     |
+----+-------------+-------+--------+---------------+---------+---------+---------------------+-------+--------------------------------+
3 rows in set ( 0.00 sec )
 这里显示了Mysql先将C表读入查询,并使用PRIMARY索引,然后联合A表进行查询,这时候type显示的是ALL,可以用的索引有idx_catid,但是实际没有用。
原因非常明显,因为使用的连接条件是A.sectionid=C.id,所以我们给A.sectionid加个索引先。
mysql > alter table jos_content add index idx_section ( ` sectionid ` ) ;
Query OK , 46585 rows affected ( 0.89 sec )
Records : 46585   Duplicates : 0   Warnings : 0
 
mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B , jos_sections C where A . catid = B . id and A . sectionid = C . id ;
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
| id | select_type | table | type    | possible_keys          | key          | key_len | ref                  | rows   | Extra        |
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
|  1 | SIMPLE       | C      | index   | PRIMARY                | PRIMARY      | 4        | NULL                 |      2 | Using index |
|  1 | SIMPLE       | A      | ref     | idx_catid , idx_section | idx_section | 4        | joomla_test . C . id     | 23293 | Using where |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY                | PRIMARY      | 4        | joomla_test . A . catid |      1 | Using where |
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
3 rows in set ( 0.00 sec )
 这时候显示结果告诉我们,效果很明显,在连接A表时type变成了ref,索引使用了idx_section,如果我们注意看后两列,对A表的查询结果后一次明显少了一半左右,而且没有用到join buffer。
这个表读入的顺序是Mysql优化器帮我们做的,可以得知,用记录数少的表做为基础表进行联合,将会得到更高的效率。
对于上面的语句,我们换一种写法
mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categories B on A . catid = B . id left join jos_sections C on A . sectionid = C . id ;
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+-------------+
| id | select_type | table | type    | possible_keys | key      | key_len | ref                      | rows   | Extra        |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+-------------+
|  1 | SIMPLE       | A      | ALL     | NULL           | NULL     | NULL     | NULL                     | 46585 |             |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY        | PRIMARY | 4        | joomla_test . A . catid      |      1 |             |
|  1 | SIMPLE       | C      | eq_ref | PRIMARY        | PRIMARY | 4        | joomla_test . A . sectionid |      1 | Using index |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+-------------+
3 rows in set ( 0.00 sec )
 Mysql读入表的顺序被改变了,这意味着,如果我们用left join来做连接查询,Mysql会按SQL语句中表出现的顺序读入,还有一个有变化的地方是联接B和C的type都变成了eq_ref,前边我们说过, 这样说明Mysql可以找到唯一的行,这个效率是比ref要高的。
 
分享到:
评论

相关推荐

    MySQL技术内幕 SQL编程及优化.pdf

    1.基础篇 1.1 explain执行计划 1.2 sq|编程 1.3数据类型 1.4查询处理 1.5子查询 1.6联接与集合操作 1.7聚合和旋转操作 2. SQL优化 2.1优化SQL的一般步骤 2.2 索引问题. 2.3两个常用的优化技巧 2.4常用...

    MySQL SQL语句优化explain关键字

    而我们在不考虑进行分库分表的操作时,进行SQL语句优化是一个很好的解决办法,下面介绍explain关键词分析SQL语句,及使用索引进行优化查询。 explain关键字使用 explain使用格式 EXPLAIN SELECT * FROM SCORE ...

    大牛出手Oracle SQL优化实例讲解

    4.EXPLAIN验证SQL是否走索引 5.结合autotrace创建并验证函数索引 6.sql trace分析工具--TKPROF详细讲解 7.V$SQL视图详解加几个实例 8.autotrace验证压缩表性能 9.autotrace验证消除子查询后的性能 10.基于基本的优化...

    Explain详解与索引最佳实践.docx

    使用EXPLAIN关键字可以模拟优化器执行SQL语句,从而知道MySQL是 如何处理你的SQL语句的。分析你的查询语句或是结构的性能瓶颈。 文档主要是使用 explain 的例子和索引的最佳实践

    Explain详解与索引最佳实践.zip

    使用EXPLAIN关键字可以模拟优化器执行SQL语句,从而知道MySQL是 如何处理你的SQL语句的。分析你的查询语句或是结构的性能瓶颈

    SQL性能优化

     以上两个SQL中dy_dj(电压等级)及xh_bz(销户标志)两个字段都没进行索引,所以执行的时候都是全表扫描,第一条SQL的dy_dj = '1KV以下'条件在记录集内比率为99%,而xh_bz=1的比率只为0.5%,在进行第一条SQL的...

    ORACLE SQL性能优化系列(全)

    优化器的选择、共享SQL语句、访问Table的方式、减少访问数据库的次数、使用DECODE函数、减少对表的查询、通过内部函数提高SQL效率、用NOT EXISTS替代NOT IN、使用EXPLAIN PLAN分析SQL语句、索引及索引的效率、使用...

    【大厂面试题】MySQL执行计划及SQL优化

    知识点标签:explain、sql优化、索引、sql性能问题 题目描述 MySQL执行计划及SQL优化 1.SQL语句表头运行一个explain时,执行后所显示的表头字段如下: id : select查询的序列号,包含一组数字,表示查询中执行select...

    MySQL如何基于Explain关键字优化索引功能

    可以帮助选择更好的索引和写出更优化的查询语句。简单讲,它的作用就是分析查询性能。  explain关键字的使用方法很简单,就是把它放在select查询语句的前面。  mysql查看是否使用索引,简单的看type类型就可以。...

    mysql的explain分析sql语句性能(慢查询和加没加索引)

    mysql的explain分析sql语句性能(慢查询和加没加索引)

    Explain详解与索引实践.pdf

    使用EXPLAIN关键字可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈 在 select 语句之前增加 explain 关键字,MySQL 会在查询上设置一个标记,执行查询会返 回执行计划的信息,而不是执行这条...

    explain分析sql效率的方法

    Explain命令在解决数据库性能上是第一推荐使用命令,大部分的性能问题可以通过此命令来简单的解决,Explain可以用来查看SQL语句的执行效 果,可以帮助选择更好的索引和优化查询语句,写出更好的优化语句。...

    小米soar SQL 优化建议工具

    大家都知道数据库中的explain执行计划工具,能帮我们分析自己所编写SQL的性能,如索引是否使用了、临时表是否使用了、文件排序是否使用了、扫描的行数以及各个查询的执行顺序等一些SQL执行反馈信息,但这个explain...

    “数据分析师”需要知道的SQL优化问题

       3)SQL优化—主要就是优化索引    4)索引的弊端    5)索引的优势    6)B+树图示说明   3、索引的分类与创建    1)索引分类    2)创建索引的两种方式    3)索引删除和索引查询   4...

    Explain详解与索引最佳实践1

    Explain工具介绍使用EXPLAIN关键字可以模拟优化器执行SQL语句,分析你的查询语句或是结构的性能瓶颈在 select 语句之前增加 explain 关

    记录一次SQL优化

    一大早到公司,加班一宿的同事跟我抱怨,说,一个表只有8w条数据,需要更新一列,根据目前进度估算,大概需要3个小时,即使...  5、执行sql语句,还是很慢,explain显示可能能用到我们刚才添加的索引,但是rows上来看

    【大厂面试题】史上最详细的一线大厂Mysql面试题详解及其答案MySQL执行计划及SQL优化

    知识点标签:explain、sql优化、索引、sql性能问题 题目描述 MySQL执行计划及SQL优化 子查询 关联查询 效率问题 子查询就是查询中有嵌套的查询,表连接都可以使用子查询,但不是所有子查询都能用表连接替 换,子查询...

    MySQL索引优化Explain详解

    找出这些SQL语句并不意味着完事了,些时我们常常用到explain这个命令来查看一个这些SQL语句的执行计划,查看该SQL语句有没有使用上了索引,有没有做全表扫描,这都可以通过explain命令来查看。所以我们深入了解MySQL...

    MySQL优化技巧大揭秘实战课视频.zip

    1-1 SQL优化基本介绍 1-2 七个查询命令特征 1-3 查询语句执行特征 1-4 需要优化的查询命令 2-1 索引基本介绍 2-2 索引分类 2-3 聚簇索引与非聚簇索引区别 2-4 主键索引与唯一性索引 2-5 单字段索引与符合索引区别 2-...

Global site tag (gtag.js) - Google Analytics