數庫庫一共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)。