5. 创建实例表:
CREATE TABLE City (
ID int(11) NOT NULL AUTO_INCREMENT,
Name char(35) NOT NULL DEFAULT '',
CountryCode char(3) NOT NULL DEFAULT '',
District char(20) NOT NULL DEFAULT '',
Population int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (ID),
KEY CountryCode (CountryCode)
) Engine=InnoDB;
www.2345.com
6. 索引实例
• 使用一个索引的情况(这也是最好的情况)
mysql> explain select * from City where ID = 1;
+-------+-------+-------------------+------------+-----------+----------+-------+-------+
| table | type | possible_keys | key
| key_len | ref
| rows | Extra |
+-------+-------+-------------------+-------------+-----------+---------+-------+-------+
| City | const | PRIMARY
| PRIMARY | 4
| const | 1
|
|
+-------+-------+-------------------+-------------+-----------+---------+-------+-------+
mysql> explain select * from City where CountryCode = 'USA';
+-------+------+------------------+-----------------+----------+--------+---------+-----------------+
| table | type | possible_keys | key
| key_len| ref
| rows | Extra
|
+-------+------+------------------+-----------------+----------+--------+----------+-----------------+
| City | ref | CountryCode | CountryCode | 3
| const | 274
| Using where |
+-------+------+------------------+------------------+---------+---------+---------+-----------------+
www.2345.com
7. 联合索引
• 最左联合索引
mysql> alter table City add key
comb(CountryCode, District, Population),
drop key CounryCode;
www.2345.com
8. 联合索引实例一
• 最左联合索引
mysql> explain select * from City
where CountryCode = 'USA'G
********************** 1. row ******************
table: City
使用了comb 的最左一列索引
type: ref
possible_keys: comb
key: comb
key_len: 3
ref: const
rows: 273
www.2345.com
10. 联合索引实例三
• 最左两列索引被使用
mysql> explain select * from City
where CountryCode = 'USA' and District = 'California'G
********************** 1. row ******************
table: City
索引`Comb`最左两列索引被使用
type: ref
CountryCode = 3 chars
possible_keys: comb
District = 20 chars
key: comb
Total = 23
key_len: 23
ref: const,const
rows: 68
www.2345.com
11. 联合索引实例四
• 三列索引同事被使用
mysql> explain select * from City
where CountryCode = 'USA' and District = 'California’
and population > 10000G
********************** 1. row ******************
table: City
type: range
这里使用到了 comb 索引的所有列:
possible_keys: comb
CountryCode = 3 chars/bytes
District = 20 chars/bytes
key: comb
Population = 4 bytes (INT)
key_len: 27
Total = 27
ref: NULL
rows: 68
www.2345.com
12. 联合索引实例五
• 无法使用联合索引的情况——没有最左部分
mysql> explain select * from City where
District = 'California' and population > 10000G
********************** 1. row ******************
table: City
CountryCode 列没有出现在
type: ALL
where
possible_keys: NULL
子句里,所以无法使用到 comb
key: NULL
这个联合索引
key_len: NULL
ref: NULL
rows: 3868
www.2345.com
13. 覆盖索引实例:一
• 覆盖索引: 索引里包含查询所使用的列
select name from City where CountryCode = 'USA‘ and
District = 'Alaska' and population > 10000
mysql> alter table City add key
cov1(CountryCode, District, population, name);
索引里包含查询所使用的列规则:
1. Where 部分
2. Group By/Order (上面例子中未使用)
3. Select 部分 (这里为: name)
www.2345.com
14. 覆盖索引实例:二
• Explain 信息
mysql> explain select name from City where CountryCode =
'USA' and District = 'Alaska' and population > 10000G
*************************** 1. row ***********
table: City
Using index : 这信息表明使用
type: range
了覆盖索引
possible_keys: cov1
MySQL 将仅使用索引获取数据,
key: cov1
不再直接去读取数据文件
key_len: 27
ref: NULL
rows: 1
Extra: Using where; Using index
www.2345.com
15. 索引列规则:一
Range 和 “const” 扫描: 有效的索引基数cardinality
• select * from City where district = 'California'
and population > 30000
• 索引 (district, population)
• 经验法则: “Const” 最好, “Range” 次之
—— 根据具体查询判断
www.2345.com
16. 索引列规则:二
• mysql> alter table City add key comb1(district, population);
mysql> explain select name from City where
district = 'California' and population > 10000G
********************** 1. row ***************************
table: City
type: range
这是比较理想的情况,索引起
possible_keys: comb1
到限制读取行数的作用
Key_len = 24 – 使用到两列索引
key: comb1
20 + 4
key_len: 24
ref: NULL
rows: 68
www.2345.com
17. 索引列规则:二
• mysql> alter table City add key comb2(population, district);
mysql> explain select name from City where
district = 'California' and population > 3000G
table: City
type: ALL
possible_keys: comb2
key: NULL
key_len: NULL
ref: NULL
rows: 4162
Extra: Using where
这种是较糟糕的情况,mysql优化器选择
不使用索引
Why?
MySQL 仅能使用到 “population” 部分
population > 3000 的城市太多,mysql
认为使用索引反而会影响效率
www.2345.com
18. 索引选择度实例
•
索引选择(Cardinality) = 记录列唯一值的数量,这是个预估值
mysql> alter table City
add key comb2(population, District);
explain select name from City where
District = 'California' and population > 1000000G
*********************** 1. row ***************************
数字加大后,使用到了索引
table: City
但是:
type: range
key_len = 4 (INT)
possible_keys: comb2
仅仅 population 部分被使用
key: comb2
key_len: 4
ref: NULL
rows: 237
Extra: Using where
www.2345.com
19. 处理低效查询
… Group By …
… Order By …
Select distinct …
Temporary
tables
Filesort
www.2345.com
20. GROUP BY 及临时表
• 每个国家有各多少城市
mysql> explain select CountryCode, count(*) from City
group by CountryCodeG
id: 1
select_type: SIMPLE
table: City
出现临时表,这样的查询效率
type: ALL
就比较低
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 4079
Extra: Using temporary; Using filesort
www.2345.com
27. 分组查询实例:二
• Explain select max(DepDelayMinutes), carrier, dayofweek
from ontime_2012
where dayofweek = 7
group by Carrier
type: ALL
全表扫描
possible_keys: NULL
使用临时表
key: NULL
key_len: NULL
ref: NULL
rows: 951353
Extra: Using where; Using temporary; Using filesort
www.2345.com
28. 添加索引: 修正全表扫描
mysql> alter table ontime_2012 add key (dayofweek);
mysql> explain select max(DepDelayMinutes), Carrier,
dayofweek from ontime_2012 where dayofweek =7
group by CarrierG
type: ref
很好的使用了索引
possible_keys: DayOfWeek
但是依然使用到临时表
key: DayOfWeek
key_len: 2
ref: const
rows: 195112
Extra: Using where; Using temporary; Using filesort
www.2345.com
29. Group by :添加覆盖索引
mysql> alter table ontime_2012
add key covered(dayofweek, Carrier, DepDelayMinutes);
mysql> explain select max(DepDelayMinutes), Carrier, dayofweek from
ontime_2012 where dayofweek =7 group by CarrierG
...
possible_keys: DayOfWeek,covered
Mysql没有使用到临时
key: covered
表,通过索引获取数据
key_len: 2
ref: const
rows: 905138
Extra: Using where; Using index
这种情况成为 “tight index scan” (紧凑所以扫描)
www.2345.com
30. 用覆盖索引不理想的情况
mysql> explain select max(DepDelayMinutes), Carrier,
dayofweek from ontime_2012
where dayofweek > 3 group by Carrier, dayofweekG
...
type: range
mysql使用了范围扫
possible_keys: covered
描
key: covered
key_len: 2
ref: NULL
rows: 2441781
Extra: Using where; Using index; Using temporary;Using
filesort
www.2345.com
31. GROUP BY: 松散索引扫描
• 松散索引(loose index)扫描:
当 MySQL 完全利用索引扫描来实现 GROUP BY 的时候,仅使用部分索引键即
可完成操作得出结果
• 要使用松散索引,最少需满足一下条件:
查询针对一个单表
GROUP BY 条件字段必须在同一个索引中最前面的连续位置
在使用GROUP BY 的同时,如果有聚合函数,只能使用 MAX 和 MIN 这
两个聚合函数,并且它们均指向相同的列
如果where条件中引用到了该索引中GROUP BY 条件之外的字段条件的
时候,必须以常量形式存在,但MIN()或MAX() 函数的参数例外
如果查询中有where条件,则条件必须为索引,不能包含非索引的字段
www.2345.com
32. 松散索引扫描实例:
mysql> alter table ontime_2012 add key loose_index_scan
(Carrier, dayofweek, DepDelayMinutes);
mysql> explain select max(DepDelayMinutes), Carrier,
dayofweek from ontime_2012 where dayofweek > 5 group
by Carrier, dayofweekG
...
select_type: SIMPLE
table: ontime_2012
这是一个很快的松散索
type: range
引扫描 ,“Range” 类型
工作!
possible_keys: DayOfWeek
key: loose_index_scan
key_len: 5
ref: NULL
rows: 359
Extra: Using where; Using index for group-by
www.2345.com
35. 松散索引扫描vs. 紧凑 索引扫描
mysql> explain select max(DepDelayMinutes) as ddm, Carrier, dayofweek from ontime_2012
where dayofweek = 5 group by Carrier, dayofweekG
table: ontime_2012
type: range
possible_keys: DayOfWeek,covered
key: loose_index_scan
key_len: 5
ref: NULL
rows: 19
Extra: Using where; Using index for group-by
mysql> select ...
+-------+----------+----------------+
| ddm | Carrier | dayofweek |
+-------+----------+----------------+
| 803 | AA
|
5
|
| 432 | AS
|
5
|
…
15 rows in set (0.00 sec)
使用松散索引扫描:
loose_index_scan(`Carrier`,`Day
OfWeek`,`DepDelayMinutes`),
www.2345.com
36. 松散索引扫描vs. 紧凑 索引扫描:二
——松散索引扫描
mysql> explain select max(DepDelayMinutes) as ddm, Carrier, dayofweek from
ontime_2012
where dayofweek > 5 group by Carrier, dayofweekG
table: ontime_2012
type: range
possible_keys: DayOfWeek,covered
key: loose_index_scan
key_len: 5
ref: NULL
rows: 19
Extra: Using where; Using index for group-by
mysql> select max(DepDelayMinutes) as ddm …
+-------+----------+--------------+
| ddm | Carrier | dayofweek |
+-------+----------+--------------+
| 1443 | AA
|
6
|
| 1364 | AA
|
7
|
…
30 rows in set (0.00 sec)
使用松散索引扫描:
loose_index_scan(`Carrier`,`Day
OfWeek`,`DepDelayMinutes`),
www.2345.com
37. 松散索引扫描vs. 紧凑 索引扫描:三
——松散索引扫描
mysql>explain select max(DepDelayMinutes) as ddm, Carrier, dayofweek from
ontime_2012
where dayofweek > 5 group by Carrier, dayofweek order by ddm descG
…
table: ontime_2012
type: range
使用松散索引扫描:
possible_keys: DayOfWeek,covered
loose_index_scan(`Carrier`,`Day
key: loose_index_scan
OfWeek`,`DepDelayMinutes`),
key_len: 5
ref: NULL
rows: 19
Extra: Using where; Using index for group-by; Using temporary; Using filesort
mysql>select max(DepDelayMinutes) as ddm,…
+------+---------+-----------+
| ddm | Carrier | dayofweek |
+------+---------+-----------+
| 1443 | AA |
6|
| 1364 | AA |
7|
…
30 rows in set (0.00 sec)
www.2345.com
38. 松散索引扫描vs. 紧凑索引扫描
—— 使用临时表
mysql>explain select max(DepDelayMinutes) as ddm, Carrier, dayofweek from
ontime_2012
ignore index(loose_index_scan) where dayofweek > 5 group by Carrier, dayofweekG
…
table: ontime_2012
type: range
possible_keys: DayOfWeek,covered
key: covered
key_len: 2
ref: NULL
rows: 407712
Extra: Using where; Using index; Using temporary; Using filesort
10:13:16> select max(DepDelayMinutes) as ddm …
+------+-----------+--------------+
| ddm | Carrier | dayofweek |
+------+-----------+--------------+
| 1443 | AA |
6|
| 1364 | AA |
7|
…
30 rows in set (0.37 sec)
1. 范围扫描
2. 使用临时表,并
且使用40万条数据
排序
www.2345.com
39. 松散索引扫描vs. 紧凑索引扫描
—— 紧凑索引扫描
mysql>explain select max(DepDelayMinutes) as ddm, Carrier, dayofweek from ontime_2012 ignore
index(loose_index_scan) where dayofweek = 5 group by Carrier, dayofweekG
…
table: ontime_2012
type: ref
possible_keys: DayOfWeek,covered
key: covered
key_len: 2
ref: const
rows: 170856
Extra: Using where
10:23:00> select max(DepDelayMinutes) as ddm
+------+---------+-----------+
| ddm | Carrier | dayofweek |
+------+---------+-----------+
| 803 | AA |
5|
| 432 | AS |
5|
15 rows in set (0.14 sec)
1. 使用Covered index:
covered(`DayOfWeek`,`Carrier`,
`DepDelayMinutes`)
2. 没有使用临时表
3. 但是需要扫描17万行数据
www.2345.com
40. 其他聚合函数不支持松散索引
AVG() + Group By ——不支持loose index scan
mysql> explain select avg(DepDelayMinutes) as ddm, Carrier, dayofweek from ontime_2012 where
dayofweek >5 group by Carrier, dayofweek G
table: ontime_2012
type: range
1. No loose index scan
possible_keys: DayOfWeek,covered
2. Filter by key
key: covered
3. Group by filesort
key_len: 2
ref: NULL
rows: 407712
Extra: Using where; Using index; Using temporary; Using filesort
mysql> select avg(DepDelayMinutes) as ddm,…
+---------+---------+---------------+
| ddm | Carrier | dayofweek |
+---------+---------+---------------+
| 7.7223 | AA |
6
|
| 7.6131 | AA |
7
|
…
30 rows in set (0.50 sec)
www.2345.com
41. ORDER BY and filesort
ORDER BY 及 filesort
www.2345.com
42. mysql> explain select district, name, population from City
where CountryCode = 'USA' order by population desc limit
10G
table: City
type: ALL
有文件排序
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 4079
Extra: Using where; Using filesort
www.2345.com
43. 解决文件排序:添加索引
mysql> alter table City add key my_sort2
(CountryCode, population);
mysql> explain select district, name, population from City
where CountryCode = 'USA' order by population desc limit 10G
table: City
type: ref
key: my_sort2
无文件排序
key_len: 3
ref: const
rows: 207
Extra: Using where
www.2345.com