数库库一共5个表(tab1,tab2等等),表结构以下:mysql
mysql> desc tab1; -- 共有400行记录 +-------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | col1 | varchar(15) | YES | | NULL | | | col2 | datetime | YES | MUL | NULL | | +-------+-------------+------+-----+---------+----------------+ 3 rows in set (0.01 sec) mysql> desc tab2; -- 共有4000行记录 +---------+-------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +---------+-------------+------+-----+---------+----------------+ | id | int(11) | NO | PRI | NULL | auto_increment | | col1 | varchar(15) | YES | | NULL | | | col2 | datetime | YES | MUL | NULL | | | tab1_id | int(11) | YES | | NULL | | +---------+-------------+------+-----+---------+----------------+ 4 rows in set (0.01 sec) -- tab3,tab4,tab5结构相似于tab2,分别有40000、400000,4000000行记录;
看第一个例子:
sql
mysql> EXPLAIN -> SELECT COUNT(*) -> FROM tab5 JOIN tab4 ON tab4.id = tab5.tab4_id -> WHERE tab5.id <= 500000; +----+-------------+-------+--------+---------------+---------+---------+-------------------+--------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+-------------------+--------+-------------+ | 1 | SIMPLE | tab5 | range | PRIMARY | PRIMARY | 4 | NULL | 504980 | Using where | | 1 | SIMPLE | tab4 | eq_ref | PRIMARY | PRIMARY | 4 | test.tab5.tab4_id | 1 | Using index | +----+-------------+-------+--------+---------------+---------+---------+-------------------+--------+-------------+ 2 rows in set (0.00 sec)
查看上面EXPLAIN指令的输出结果,一共有两行, 先后顺序也是MySQL扫描表的前后顺序 (1) 。共有两种扫描类型“range”、“eq_ref”,说明MySQL先使用tab5表的主键索引来过滤条件(<=500000),而后再利用参照tab4的主键索引来找到关联表的记录。spa
再来看一个例子:code
mysql> EXPLAIN -> SELECT COUNT(*) -> FROM tab5 JOIN tab4 ON tab4.id = tab5.tab4_id -> WHERE tab5.id <= 1000000; +----+-------------+-------+--------+---------------+---------+---------+-------------------+---------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+--------+---------------+---------+---------+-------------------+---------+-------------+ | 1 | SIMPLE | tab5 | ALL | PRIMARY | NULL | NULL | NULL | 4000000 | Using where | | 1 | SIMPLE | tab4 | eq_ref | PRIMARY | PRIMARY | 4 | test.tab5.tab4_id | 1 | Using index | +----+-------------+-------+--------+---------------+---------+---------+-------------------+---------+-------------+ 2 rows in set (0.00 sec)
和前面的EXPLAIN语句相比,把查询的范围扩大了一倍。再看扫描类型,变化为“ALL”,“eq_ref”,这说明MySQL再扫描tab5时不使用主键索引了,直接扫描全表,而后拿记录来过滤条件(<=1000000),而后再利用参照tab4的主键索引来找到关联表的记录。为何不使用tab5的主键索引了呢?想必是MySQL断定在索引(二叉树)中查找一个范围所用的时间比扫描全表还慢,因此选择扫描全表(2)。索引
下面用“强制使用索引”来验证下上面的想法:rem
mysql> SELECT COUNT(*) FROM tab5 JOIN tab4 ON tab4.id = tab5.tab4_id WHERE tab5.id <= 1000000; 1 row in set (1.73 sec) mysql> SELECT COUNT(*) FROM tab5 FORCE INDEX (PRIMARY) JOIN tab4 ON tab4.id = tab5.tab4_id WHERE tab5.id <= 1000000; 1 row in set (4.02 sec)
结果和设想的一致,索引的较大范围断定仍是比较耗时的。io
看下一条EXPLAIN语句:table
mysql> EXPLAIN -> SELECT COUNT(*) -> FROM tab5 JOIN tab4 ON tab4.id = tab5.tab4_id -> WHERE tab5.id <= 2000000 AND tab4.id = 200000; +----+-------------+-------+-------+---------------+---------+---------+-------+---------+-------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+-------+-------+---------------+---------+---------+-------+---------+-------------+ | 1 | SIMPLE | tab4 | const | PRIMARY | PRIMARY | 4 | const | 1 | Using index | | 1 | SIMPLE | tab5 | ALL | PRIMARY | NULL | NULL | NULL | 4000000 | Using where | +----+-------------+-------+-------+---------------+---------+---------+-------+---------+-------------+ 2 rows in set (0.00 sec)
如前面的相比,我指定查询条件tab4的一条(id = 200000)记录,此时MySQL的扫描顺序发生的变化,先扫描tab4,再扫描tab5,扫描类型也变为“const”,“ALL”。也就是说MySQL先根据tab4的索引找到id等于200000的记录,而后再对tab5表进行全表扫描,逐条比较是否知足关联条件和id小于等2000000的条件。来看下这个查询语句须要的时间:class
mysql> SELECT COUNT(*) FROM tab5 JOIN tab4 ON tab4.id = tab5.tab4_id WHERE tab5.id <= 2000000 AND tab4.id = 200000; 1 row in set (0.52 sec)
若是,我对tab5表中的tab4_id字段作一个索引,会不会让上面语句查询速度有很大的提升呢?试一下,再看查询速度:test
-- 原来tab5表tab4_id字段没有索引,这条语句是在加上索引后执行的 mysql> SELECT COUNT(*) FROM tab5 JOIN tab4 ON tab4.id = tab5.tab4_id WHERE tab5.id <= 2000000 AND tab4.id = 200000; 1 row in set (0.00 sec)
速度果真有很大提升,已经不超过10毫秒了。再执行下EXPLAIN语句:
mysql> EXPLAIN -> SELECT COUNT(*) -> FROM tab5 JOIN tab4 ON tab4.id = tab5.tab4_id -> WHERE tab5.id <= 2000000 AND tab4.id = 200000; +----+-------------+-------+-------+---------------------+-------------+---------+-------+------+------------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra +----+-------------+-------+-------+---------------------+-------------+---------+-------+------+------------- | 1 | SIMPLE | tab4 | const | PRIMARY | PRIMARY | 4 | const | 1 | Using index | 1 | SIMPLE | tab5 | ref | PRIMARY,idx_tab4_id | idx_tab4_id | 5 | const | 10 | Using where +----+-------------+-------+-------+---------------------+-------------+---------+-------+------+------------- 2 rows in set (0.00 sec)
此时的扫描类型已经变为“const”,“ref”,也就是说MySQL先根据tab4的索引找到id等于200000的记录,而后再参照tab4的id,利用tab5表中的对应索引划定一个范围,在这个范围中再比较其是否知足条件(<=2000000)。也提示咱们关联字段要不要建索引,视关联表在查询时的使用条件而定(3)。