该文章针对InnoDB存储引擎B树索引的优化,前提要对B树索引有一定的理解。
这是我们写SQL的流程:
SELECT a.`name`
FROM a
LEFT JOIN b
ON a.id=b.aid
WHERE a.name='xxx'
GROUP BY a.name
HAVING b.name!='xxx'
ORDER BY a.id
LIMIT 0,10
机器对查询SQL的执行加载顺序:
id | name |
---|---|
1 | 令狐冲 |
2 | 东方不败 |
b表有2条数据:
id | name | a_id |
---|---|---|
1 | 独孤九剑 | 1 |
2 | 吸星大法 | 1 |
计算出笛卡尔结果为:
a.id | a.name | b.id | b.name | b.a_id |
---|---|---|---|---|
1 | 令狐冲 | 1 | 独孤九剑 | 1 |
1 | 令狐冲 | 2 | 吸星大法 | 1 |
2 | 东方不败 | 1 | 独孤九剑 | 1 |
2 | 东方不败 | 2 | 吸星大法 | 1 |
a.id | a.name | b.id | b.name | b.a_id |
---|---|---|---|---|
1 | 令狐冲 | 1 | 独孤九剑 | 1 |
1 | 令狐冲 | 2 | 吸星大法 | 1 |
a.id | a.name | b.id | b.name | b.a_id |
---|---|---|---|---|
1 | 令狐冲 | 1 | 独孤九剑 | 1 |
1 | 令狐冲 | 2 | 吸星大法 | 1 |
2 | 东方不败 | null | null | null |
SELECT a.id,b.name FROM a INNER JOIN b on a.id=b.a_id
SELECT a.id,b.name FROM a LEFT JOIN b on a.id=b.a_id
SELECT a.id,b.name FROM a LEFT JOIN b on a.id=b.a_id where b.a_id is NULL
SELECT a.id,b.name FROM a RIGHT JOIN b on a.id=b.a_id
取右表的全集:
SELECT a.id,b.name FROM a RIGHT JOIN b on a.id=b.a_id where a.id is NULL
SELECT a.id,b.name FROM a FULL OUTER JOIN b on a.id=b.a_id
取两个表的并集:
SELECT a.id,b.name FROM a FULL OUTER JOIN b on a.id=b.a_id where a.id is NULL OR b.a_id is NULL
使用EXPLAIN关键字可以模拟优化器执行SQL查询语句,从而知道MySQL是如何处理你的SQL语句的。从而可以分析SQL语句的优劣。简单的说EXPLAIN可以看到优化器优化后的查询计划。要对SQL进行优化,理解EXPLAIN是关键。
能够看到哪些信息:
EXPLAIN SELECT *FROM t1
select查询的序列号,包含一组数字,表示查询中执行select子句或者操作表的顺序。
有以下三种情况:
EXPLAIN SELECT a.id,b.name FROM t1 a INNER JOIN t2 b ON a.id=b.`t1_id`
EXPLAIN SELECT *FROM t1 WHERE id=(SELECT id FROM t2 LIMIT 1)
EXPLAIN SELECT a.id,b.name FROM t1 a INNER JOIN t2 b ON a.id=b.`t1_id` WHERE a.id = (SELECT id FROM t2 LIMIT 1)
ID相同,认为是同一组,从上往下顺序执行,ID不同,从大往小执行。
查询的类型,主要用于区别普通查询、子查询、联合查询等的复杂查询。
EXPLAIN SELECT id FROM t1
PRIMARY:查询中若包含子查询,则最外面的查询就是PRIMARY。可以看上面例子。
SUBQUERY:在select或者where中包含了子查询,上面已经有了where的情况,下面再试试select的情况。
EXPLAIN SELECT id,(SELECT `name` FROM t2 WHERE id=1) t2_name FROM t1
EXPLAIN SELECT id FROM t1 WHERE id=1 UNION SELECT id FROM t1 WHERE id=1
查询的表。
显示使用了何种类型。这个是调优最重要的标准之一。
下面是值,好处从上到下,也就是从快到慢。
EXPLAIN SELECT *FROM t2 WHERE id=1 #id是主键
EXPLAIN SELECT *FROM t2 WHERE NAME='yehaocong' #name是唯一索引
EXPLAIN SELECT t1.* FROM t1 INNER JOIN t2 WHERE t1.id=t2.`t1_id`
EXPLAIN SELECT *FROM t1 WHERE t1.`t2_id`=1 #t2_id是一个普通索引
EXPLAIN SELECT *FROM t1 WHERE id>=1
EXPLAIN SELECT id FROM t1
EXPLAIN SELECT *FROM t2 WHERE t1_id=1 #t1_id没有建立索引
显示可能应用到这张表的索引,一个或者多个,查询涉及到的字段上若存在索引,该索引就会被列出,但不一定被实际使用到。
EXPLAIN SELECT *FROM t1 WHERE t1.`name`='YeHaoxian23'
查询实际上用到的索引。查看上面例子。
索引的大小字节数,可通过该列计算查询中使用的索引长度,在不损失精确性的情况下,长度越短约好。key_len显示的值为索引字段的最大可能长度,也就是建表时该字段设置的长度。而并非实际长度。
有一个联合索引 (name,desc)
EXPLAIN SELECT *FROM t1 WHERE t1.`name`='YeHaoxian23' #查询name为YeHaoxian23的值
只用到了联合索引中的name字段,key_len 为403.
EXPLAIN SELECT *FROM t1 WHERE t1.`name`='YeHaoxian23' AND t1.`desc`='2dad'
显示索引中的哪一列被使用了。
根据表统计信息及索引使用情况,大致估算出找到所需记录所需要读取的行数,越少越好。
过滤率,实际符合条件的行数/扫描的函数。
EXPLAIN SELECT *FROM t2 WHERE t2.`desc`='1aa'
包含不适合在其他列显示的但十分重要的额外信息
EXPLAIN SELECT *FROM t2 ORDER BY t2.`t1_id` #t1_id没有建立索引
EXPLAIN SELECT *FROM t1 WHERE t1.`name`='YeHaoxian23' ORDER BY t1.`t2_id` #有联合索引(name,desc,t2_id),因为name和t2_id之间隔了一个,所以就导致了文件排序。
EXPLAIN SELECT *FROM t1 WHERE t1.`name`='YeHaoxian23' AND t1.`desc`='2dad' ORDER BY t1.`t2_id` # 联合索引连续了,所以不用文件排序。
EXPLAIN SELECT *FROM t1 WHERE t1.`name`='YeHaoxian23' ORDER BY t1.`desc`,t1.`t2_id` #这样也不会导致文件排序
EXPLAIN SELECT *FROM t1 WHERE id=1 UNION SELECT *FROM t1 WHERE id=2 #生成Union临时表
EXPLAIN SELECT `desc` FROM t1 GROUP BY t1.`desc` #还是那个联合索引
EXPLAIN SELECT t1.`name`,t1.`desc` FROM t1 GROUP BY t1.`name`,t1.`desc` #使用到了
EXPLAIN SELECT t1.`name`,t1.`desc` FROM t1 WHERE t1.`name`='YeHaoxian23' AND t1.`desc`='2dad' #还是那个联合索引
EXPLAIN SELECT `desc` FROM t1 WHERE t1.`desc`='2dad' #如果出现了Using where 和 Using index 表明使用覆盖索引,并且索引被用来执行索引键查找。
EXPLAIN SELECT `desc` FROM t1 WHERE 1!=1 #1!=1总是返回false
总的来说这个字段就是避免 文件排序和生成临时表,追求index。