EXPLAIN sql優化方法(2) Using temporary ; Using filesort


優化GROUP BY語句
   默認情況下,MySQL對所有GROUP BY col1,col2...的字段進行排序。這與在查詢中指定ORDER BY col1,col2...類似。因此,如果顯式包括一個包含相同的列的ORDER BY子句,則對MySQL的實際執行性能沒有什么影響。 如果查詢包括GROUP BY 但用戶想要避免排序結果的消耗,則可以指定ORDER By NULL禁止排序,例如:
explain select id, sum(moneys) from sales2 group by id \G
explain select id, sum(moneys) from sales2 group by id order by null \G
你可以通過比較發現第一條語句會比第二句在Extra:里面多了Using filesort.而恰恰filesort是最耗時的。
 
優化ORDER BY語句
    在某些情況中,MySQL可以使用一個索引來滿足ORDER BY子句,而不需要額外的排序。WHERE 條件和 ORDER BY使用相同的索引,並且ORDER BY的順序和索引順序相同,並且ORDER BY的字段都是升序或者都是降序。
例如:
SELECT * FROM t1 ORDER BY key_part1,key_part2,....:
SELECT * FROM t1 WHERE key_part1 = 1 ORDER BY key_part1 DESC,key_part2 DESC;
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 DESC;
但是以下的情況不使用索引:
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;
--ORDER by的字段混合ASC 和 DESC
SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
----用於查詢行的關鍵字與ORDER BY 中所使用的不相同
SELECT * FROM t1 ORDER BY key1, key2;
----對不同的關鍵字使用ORDER BY
 
mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categoriesB on A . catid = B . id left join jos_sections C on A . sectionid = C . id order by B . id ;
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+---------------------------------+
| id | select_type | table | type    | possible_keys | key      | key_len | ref                      | rows   | Extra                            |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+---------------------------------+
|  1 | SIMPLE       | A      | ALL     | NULL           | NULL     | NULL     |NULL                     | 46585 | Using temporary ; Using filesort |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY        | PRIMARY | 4        |joomla_test . A . catid      |      1 |                                 |
|  1 | SIMPLE       | C      | eq_ref | PRIMARY        | PRIMARY | 4        |joomla_test . A . sectionid |      1 | Using index                      |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+---------------------------------+
3 rows in set ( 0.00 sec ) 
 
mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categoriesB on A . catid = B . id left join jos_sections C on A . sectionid = C . id order by A . id ;
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+----------------+
| id | select_type | table | type    | possible_keys | key      | key_len | ref                      | rows   | Extra           |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+----------------+
|  1 | SIMPLE       | A      | ALL     | NULL           | NULL     | NULL     |NULL                     | 46585 | Using filesort |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY        | PRIMARY | 4        |joomla_test . A . catid      |      1 |                |
|  1 | SIMPLE       | C      | eq_ref | PRIMARY        | PRIMARY | 4        |joomla_test . A . sectionid |      1 | Using index     |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+-------+----------------+


對於上面兩條語句,只是修改了一下排序字段,而第一個使用了Using temporary,而第二個卻沒有。在日常的網站維護中,如果有Using temporary出現,說明需要做一些優化措施了。
而為什么第一個用了臨時表,而第二個沒有用呢?
因為如果有ORDER BY子句和一個不同的GROUP BY子句,或者如果ORDER BY或GROUP BY中的字段都來自其他的表而非連接順序中的第一個表的話,就會創建一個臨時表了。
那么,對於上面例子中的第一條語句,我們需要對jos_categories的id進行排序,可以將SQL做如下改動:

mysql > explain select B . id , B . title , A . title from jos_categories A left join jos_contentB on A . id = B . catid left join jos_sections C on B . sectionid = C . id order by A . id ;
+----+-------------+-------+--------+---------------+-----------+---------+-------------------------+------+----------------+
| id | select_type | table | type    | possible_keys | key        | key_len | ref                      | rows | Extra           |
+----+-------------+-------+--------+---------------+-----------+---------+-------------------------+------+----------------+
|  1 | SIMPLE       | A      | ALL     | NULL           | NULL       | NULL    | NULL                     |    18 | Using filesort |
|  1 | SIMPLE       | B      | ref     | idx_catid      | idx_catid | 4        |joomla_test . A . id         | 3328 |                |
|  1 | SIMPLE       | C      | eq_ref | PRIMARY        | PRIMARY    | 4        |joomla_test . B . sectionid |    1 | Using index     |
+----+-------------+-------+--------+---------------+-----------+---------+-------------------------+------+----------------+
3 rows in set ( 0.00 sec )


這樣我們發現,不會再有Using temporary了,而且在查詢jos_content時,查詢的記錄明顯有了數量級的降低,這是因為jos_content的idx_catid起了作用。
所以結論是:

盡量對第一個表的索引鍵進行排序,這樣效率是高的。
我們還會發現,在排序的語句中都出現了Using filesort,字面意思可能會被理解為:使用文件進行排序或中文件中進行排序。實際上這是不正確的,這是一個讓人產生誤解的詞語。
當我們試圖對一個沒有索引的字段進行排序時,就是filesoft。它跟文件沒有任何關系,實際上是內部的一個快速排序。
然而,當我們回過頭來再看上面運行過的一個SQL的時候會有以下發現:

mysql > explain select A . id , A . title , B . title from jos_content A , jos_categories B ,jos_sections C where A . catid = B . id and A . sectionid = C . id order by C . id ;
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
| id | select_type | table | type    | possible_keys          | key          | key_len| ref                  | rows   | Extra        |
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
|  1 | SIMPLE       | C      | index   | PRIMARY                | PRIMARY      | 4        | NULL                 |      1 | Using index |
|  1 | SIMPLE       | A      | ref     | idx_catid , idx_section | idx_section | 4       | joomla_test . C . id     | 23293 | Using where |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY                | PRIMARY      |4        | joomla_test . A . catid |      1 | Using where |
+----+-------------+-------+--------+-----------------------+-------------+---------+---------------------+-------+-------------+
3 rows in set ( 0.00 sec )

這是我們剛才運行過的一條語句,只是加了一個排序,而這條語句中C表的主鍵對排序起了作用,我們會發現Using filesort沒有了。
而盡管在上面的語句中也是對第一個表的主鍵進行排序,卻沒有得到想要的效果(第一個表的主鍵沒有用到),這是為什么呢?實際上以上運行過的所有left join的語句中,第一個表的索引都沒有用到,盡管對第一個表的主鍵進行了排序也無濟於事。不免有些奇怪!

於是我們繼續測試了下一條SQL:

mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categoriesB on A . catid = B . id left join jos_sections C on A . sectionid = C . id where A . id < 100 ;
+----+-------------+-------+--------+----------------+---------+---------+-------------------------+------+-------------+
| id | select_type | table | type    | possible_keys   | key      | key_len | ref                      | rows | Extra        |
+----+-------------+-------+--------+----------------+---------+---------+-------------------------+------+-------------+
|  1 | SIMPLE       | A      | range   | PRIMARY         | PRIMARY | 4        |NULL                     |    90 | Using where |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY         | PRIMARY | 4        |joomla_test . A . catid      |    1 |             |
|  1 | SIMPLE       | C      | eq_ref | PRIMARY         | PRIMARY | 4        |joomla_test . A . sectionid |    1 | Using index |
+----+-------------+-------+--------+----------------+---------+---------+-------------------------+------+-------------+
3 rows in set ( 0.05 sec )

然后,當再次進行排序操作的時候,Using filesoft也沒有再出現

mysql > explain select A . id , A . title , B . title from jos_content A left join jos_categoriesB on A . catid = B . id left join jos_sections C on A . sectionid = C . id where A . id < 100order by A . id ;
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+------+-------------+
| id | select_type | table | type    | possible_keys | key      | key_len | ref                      | rows | Extra        |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+------+-------------+
|  1 | SIMPLE       | A      | range   | PRIMARY        | PRIMARY | 4        |NULL                     |  105 | Using where |
|  1 | SIMPLE       | B      | eq_ref | PRIMARY        | PRIMARY | 4        |joomla_test . A . catid      |    1 |             |
|  1 | SIMPLE       | C      | eq_ref | PRIMARY        | PRIMARY | 4        |joomla_test . A . sectionid |    1 | Using index |
+----+-------------+-------+--------+---------------+---------+---------+-------------------------+------+-------------+
3 rows in set ( 0.00 sec )

這個結果表明:對where條件里涉及到的字段,Mysql會使用索引進行搜索,而這個索引的使用也對排序的效率有很好的提升。
寫了段程序測試了一下,分別讓以下兩個SQL語句執行200次:

  1. select A . id , A . title , B . title from jos_content   A left join jos_categories B on A .catid = B . id left join jos_sections C   on A . sectionid = C . id
  2. select   A . id , A . title , B . title from jos_content   A , jos_categories B ,jos_sections C where A . catid = B . id and   A . sectionid = C . id
  3. select   A . id , A . title , B . title from jos_content A left   join jos_categories B on A. catid = B . id left join jos_sections C on   A . sectionid = C . id   order by rand ()limit 10
  4. select   A . id from   jos_content A left join jos_categories B on B . id = A . catid leftjoin   jos_sections C on A . sectionid = C . id order by A . id

結果是第(1)條平均用時20s ,第(2)條平均用時44s ,第(3)條平均用時70s ,第(4)條平均用時2s 。而且假如我們用explain觀察第(3)條語句的執行情況,會發現它創建了temporary表來進行排序。

綜上所述,可以得出如下結論:
1. 對需要查詢和排序的字段要加索引。
2. 在一定環境下,left join還是比普通連接查詢效率要高,但是要盡量少地連接表,並且在做連接查詢時注意觀察索引是否起了作用。
3. 排序盡量對第一個表的索引字段進行,可以避免mysql創建臨時表,這是非常耗資源的。
4. 對where條件里涉及到的字段,應適當地添加索引,這樣會對排序操作有優化的作用。
5. 在做隨機抽取數據的需求時,避免使用order by rand(),從上面的例子可以看出,這種是很浪費數據庫資源的,在執行過程中用show processlist查看,會發現第(3)條有Copying to tmp table on disk。而對(3)和(4)的對比得知,如果要實現這個功能,最好另辟奚徑,來減輕Mysql的壓力。
6. 從第4點可以看出,如果說在分頁時我們能先得到主鍵,再根據主鍵查詢相關內容,也能得到查詢的優化效果。通過國外《High Performance MySQL》專家組的測試可以看出,根據主鍵進行查詢的類似“SELECT ... FROM... WHERE id = ...”的SQL語句(其中id為PRIMARYKEY),每秒鍾能夠處理10000次 以上的查詢,而普通的SELECT查詢每秒只能處理幾十次到幾百次 。涉及到分頁的查詢效率問題,網上的可用資源越來越多,查詢功能也體現出了它的重要性。也便是sphinx、lucene這些第三方搜索引擎的用武之地了。
7. 在平時的作業中,可以打開Mysql的Slow queries功能,經常檢查一下是哪些語句降低的Mysql的執行效率,並進行定期優化。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM