MySQL Using temporary; Using filesort INNER JOIN優化

問題

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

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。mysql

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

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

下面給出優化後的SQL,惟一的變化就是把鏈接方式改爲了「STRAIGHT_JOIN」:post

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順序get

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

繼續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」強制鏈接順序。

實際上在某些特殊狀況裏,排序字段能夠不在驅動表裏,好比驅動表結果集只有一行記錄,而且在鏈接其它表時,索引除了鏈接字段,還包含了排序字段,此時鏈接表後,索引中的數據自己天然就是排好序的。

既然聊到這裏順帶說點題外話,你們可能會遇到相似下面的問題:本來運行良好的查詢語句,過了一段時間後,可能會忽然變得很糟糕。一個很大可能的緣由就是數據分佈狀況發生了變化,從而致使MySQL優化器對驅動表的選擇發生了變化,進而出現索引失效的狀況,因此沒事最好多查查,關注一下這些狀況。

相關文章
相關標籤/搜索