Mysql中的STRAIGHT_JOIN和force index語句對sql的優化

1.首先轉載下:火丁筆記的文件https://huoding.com/2013/06/04/261;來給大家介紹下STRAIGHT_JOIN的使用:

問題

通過「SHOW FULL PROCESSLIST」語句很容易就能查到問題SQL,如下:

SELECT post.*
FROM post
INNER JOIN post_tag ON post.id = post_tag.post_id
WHERE post.status = 1 AND post_tag.tag_id = 123
ORDER BY post.created DESC
LIMIT 100

說明:因爲post和tag是多對多的關係,所以存在一個關聯表post_tag。

試着用EXPLAIN查詢一下SQL執行計劃(篇幅所限,結果有刪減):

+----------+---------+-------+-----------------------------+
| table    | key     | rows  | Extra                       |
+----------+---------+-------+-----------------------------+
| post_tag | tag_id  | 71220 | Using where; Using filesort |
| post     | PRIMARY |     1 | Using where                 |
+----------+---------+-------+-----------------------------+

下面給出優化後的SQL,唯一的變化就是把連接方式改成了「STRAIGHT_JOIN」:

SELECT post.*
FROM post
STRAIGHT_JOIN post_tag ON post.id = post_tag.post_id
WHERE post.status = 1 AND post_tag.tag_id = 123
ORDER BY post.created DESC
LIMIT 100

試着用EXPLAIN查詢一下SQL執行計劃(篇幅所限,結果有刪減):

+----------+----------------+--------+-------------+
| table    | key            | rows   | Extra       |
+----------+----------------+--------+-------------+
| post     | status_created | 119340 | Using where |
| post_tag | post_id        |      1 | Using where |
+----------+----------------+--------+-------------+

對比優化前後兩次EXPLAIN的結果來看,優化後的SQL雖然「rows」更大了,但是沒有了「Using filesort」,綜合來看,性能依然得到了提升。

提醒:注意兩次EXPLAIN結果中各個表出現的先後順序,稍後會解釋。

解釋

對第一條SQL而言,爲什麼MySQL優化器選擇了一個耗時的執行方案?對第二條SQL而言,爲什麼把連接方式改成STRAIGHT_JOIN之後就提升了性能?

這一切還得從MySQL對多表連接的處理方式說起,首先MySQL優化器要確定以誰爲驅動表,也就是說以哪個表爲基準,在處理此類問題時,MySQL優化器採用了簡單粗暴的解決方法:哪個表的結果集小,就以哪個表爲驅動表,當然MySQL優化器實際的處理方式會複雜許多,具體可以參考:MySQL優化器如何選擇索引和JOIN順序

說明:在EXPLAIN結果中,第一行出現的表就是驅動表。

繼續post連接post_tag的例子,MySQL優化器有如下兩個選擇,分別是:

  • 以post爲驅動表,通過status_created索引過濾,結果集119340行
  • 以post_tag爲驅動表,通過tag_id索引過濾,結果集71220行

顯而易見,post_tag過濾的結果集更小,所以MySQL優化器選擇它作爲驅動表,可悲催的是我們還需要以post表中的created字段來排序,也就是說排序字段不在驅動表裏,於是乎不可避免的出現了「Using filesort」,甚至「Using temporary」。

知道了來龍去脈,優化起來就容易了,要儘可能的保證排序字段在驅動表中,所以必須以post爲驅動表,於是乎必須藉助「STRAIGHT_JOIN」強制連接順序。

實際上在某些特殊情況裏,排序字段可以不在驅動表裏,比如驅動表結果集只有一行記錄,並且在連接其它表時,索引除了連接字段,還包含了排序字段,此時連接表後,索引中的數據本身自然就是排好序的。

對於「STRAIGHT_JOIN」,我總覺得這種非標準的語法屬於奇技淫巧的範疇,能不用盡量不用,畢竟多數情況下,MySQL優化器都能做出正確的選擇。

2.首先轉載下:http://blog.csdn.net/bruce128/article/details/46777567;來給大家介紹下force index的使用:

 今天寫了一個統計sql,在一個近億條數據的表上執行,200s都查不出結果。SQL如下:

  1. select customer,count(1) c  
  2. from upv_**  
  3. where created between "2015-07-06" and "2015-07-07"  
  4. group by customer   
  5. having c > 20  
  6. order by c desc  

    執行explain,發現這個sql掃描了8000W條記錄到磁盤上。然後再進行篩選。type=index說明整個索引樹都被掃描了,效果顯然不理想。


    拿着這個SQL去請教項目組的數據庫大牛,僅僅加了一個force index,花了1s多就出結果了。修改後的SQL如下:

  1. select customer,count(1) c  
  2. from upv_** force index(idx_created)  
  3. where created between "2015-07-06" and "2015-07-07"  
  4. group by customer   
  5. having c > 15  
  6. order by c desc   
  1.   

    同樣執行以下explain命令,這個SQL僅僅掃描了磁盤的110W行記錄。也就是上一個SQL的80分之一。大家都知道,掃描磁盤是很耗時的IO操作,比內存操作慢幾個數量級。type=range,說明索引樹僅僅被部分掃描,要優於前面那個SQL.


    除了磁盤掃描的行數的不一樣,還有采用的索引的不用,上面的sql用的是聯合索引,而下面的是單純的created字段的索引。由於用的是created的索引,驅動條件就是created的區間,需要掃描的數據就立刻變小了,因爲時間區間小。後面的SQL的key_len要遠遠小於前面的SQL,也就意味着要掃描的磁盤上的索引數據量要遠遠小於前面的SQL。

    第一個sql使用的是錯誤的索引,帶來低效的查詢。然後每條SQL只可能使用一個索引。通過上面的分析就可以發現,force index()指令可以指定本次查詢使用哪個索引!這樣就可以避免MySql優化器用到了一個低效的索引。



總結:這兩個語句的使用都是爲了指定更好的索引和以更好的表爲基表進行提高查詢效率


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章