用Explain分析SQL語句的時候,經常發現有的語句在Extra列會出現Using filesort,根據MySQL官方文檔對他的描述:
中文手冊上翻譯的很別扭:
總的來說,Using filesort 是Mysql里一種速度比較慢的外部排序,如果能避免是最好的了,很多時候,我們可以通過優化索引來盡量避免出現Using filesort,從而提高速度。
這里舉個簡單的例子:
`id` int(10) unsigned NOT NULL auto_increment,
`room_number` int(10) unsigned NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `room_number` (`room_number`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
Query OK, 1 row affected (13.21 sec)
OK,數據准備好了,開始試驗。
由上面例子中建立的表信息,我已經建立了兩個索引,一個主鍵id,一個room_number列索引
那現在來看一條SQL,
分析一下
mysql> EXPLAIN SELECT id FROM testing WHERE room_number=1000 ORDER BY id ;
+----+-------------+---------+------+---------------+-------------+---------+-------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+---------------+-------------+---------+-------+------+-----------------------------+
| 1 | SIMPLE | testing | ref | room_number | room_number | 4 | const | 1 | Using where; Using filesort |
+----+-------------+---------+------+---------------+-------------+---------+-------+------+-----------------------------+
1 row in set (0.00 sec)
出現了Using filesort,並且用到了room_number這列索引,但是,在這里用到的索引是針對WHERE后面的room_number條件的,而最后面的排序是根據id來的,這就是手冊中說的,“額外的一次排序”!,於是就會出現Using filesort,根據我以前寫過的一文章,我再建立一個聯合索引 room_number_id
在來分析一下
mysql> EXPLAIN SELECT id FROM testing WHERE room_number=1000 ORDER BY id ;
+----+-------------+---------+------+----------------------------+----------------+---------+-------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+------+----------------------------+----------------+---------+-------+------+--------------------------+
| 1 | SIMPLE | testing | ref | room_number,room_number_id | room_number_id | 4 | const | 1 | Using where; |
+----+-------------+---------+------+----------------------------+----------------+---------+-------+------+--------------------------+
1 row in set (0.00 sec)
現在Using filesort不見了。
總結一下:
1.一般有order by語句,在索引加得不當的情況下,都有可能出現Using filesort,這時候就要對SQL語句和索引進行優化了,但是,並不是說出現Using filesort就是個嚴重的問題,不是這樣的,此次舉的例子比較極端,幾乎不太可能出現這么傻瓜的查詢,優化和不優化,要看它是不是影響了業務性能。
2. 從上面可以看到聯合索引,也可以叫多列索引,形如 key ('A1','A2','A3' ,'A4')等的,排序的思路一般是,先按照A1來排序,A1相同,然后按照A2排序,以此類推,這樣對於(A1),(A1,A2),(A1,A2,A3)的索引都是有效的,但是對於(A2,A3)這樣的索引就無效了。
需要了解MySQL 的特性:
- 一條 SQL 語句只能使用 1 個索引 (5.0-),MySQL 根據表的狀態,選擇一個它認為最好的索引用於優化查詢
- 聯合索引,只能按從左到右的順序依次使用
- 從上邊可以看到結合索引,也可以叫多列索引,形如 key ('B1','B2','B3' ,'B4')等的,排序的思路通常為,先按照B1來排序,B1相同,然后按照B2排序,以此類推,這樣對於(B1),(B1,B2), (B1,B2,B3)的索引都是有效的,可是對於(B2,B3)這樣的索引就無效了。
根據這個特性就可以解決問題:
user_id 和 item_id 是 2 個索引,我的語句中,MySQL 選擇了 user_id,那么 item_id 的索引沒有起到任何用處,所以,當我要排序的時候,由於記錄數較多,內存中的排序 buffer 滿了,只能 Using filesort 進行外部排序,因此每次查詢要從磁盤讀取幾十 M 的數據,太慢了。
修改表結構,刪除 user_id 和 item_id 的 INDEX 索引,建立一個名為 user_item 的聯合 UNIQUE 索引,順序是先 user_id 后 item_id,再 EXPLAIN,這回只有 Using where 了。