2012-08-24 117 views
0

我們有一個相對直接的查詢,可以在4個表中進行LEFT JOIN。 A是層級中的「主」表或最頂層的表。乙鏈接A,C鏈接B.此外,X鏈接答:這樣的層次結構基本上是MySQL查詢優化;選擇多個字段與JOIN

A 
C => B => A 
X => A 

查詢基本上是:

SELECT 
    a.*, b.*, c.*, x.* 
FROM 
    a 
    LEFT JOIN b ON b.a_id = a.id 
    LEFT JOIN c ON c.b_id = b.id 
    LEFT JOIN x ON x.a_id = a.id 
WHERE 
    b.flag = true 
ORDER BY 
    x.date DESC 
LIMIT 25 

通過EXPLAIN,我已經證實, 有正確的索引,並且內置的MySQL查詢優化器正確使用這些索引並正確使用

因此,這裏的怪一部分...

當我們作爲查詢運行,它需要大約1.1秒的運行。

但是,在做了一些檢查之後,似乎如果我刪除了大部分SELECT字段,我會得到一個明顯提高的顯着的

所以,如果不是我們製作成兩步查詢過程如下:

  1. 第一個查詢相同,所不同的SELECT子句更改爲只SELECT a.id,而不是SELECT *
  2. 第二個查詢也和上面一樣,除了將WHERE子句改爲只執行a.id IN查詢1的結果而不是之前的結果

結果是截然不同的。第一個查詢爲.03秒,第二個查詢爲.02。

在代碼中執行這兩步查詢本質上爲我們提供了20倍的性能提升。

因此,這裏是我的問題:

不應該在該類型的優化已經在數據庫引擎內進行?爲什麼實際選擇哪個字段的差異會影響查詢的整體性能?

在一天結束時,它只是選擇完全相同的25行並返回完全相同的25行內容。那麼,爲什麼表現差距很大呢?

ADDED 2012-08-24 13:02 PM PDT

感謝eggyal和invertedSpear的反饋。首先,這不是一個緩存問題 - 我已經運行了兩次測試,每次在兩種方法之間交替進行兩次查詢(大約10次)。第一種(單個查詢)方法的結果平均值爲1.1秒,第二種(2查詢)方法的平均值爲.03 + 0.02秒。

就索引而言,我認爲我已經做了一個EXPLAIN以確保我們通過關鍵點,而且我們大部分都是。不過,我只是再次做了一個快速檢查,一個有趣的事情需要注意:

較慢的「單個查詢」的做法不顯示額外注「使用索引」爲三線:

+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+ 
| id | select_type | table | type | possible_keys   | key    | key_len | ref       | rows | Extra          | 
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+ 
| 1 | SIMPLE  | t1 | index | PRIMARY    | shop_group_id_idx | 5  | NULL       | 102 | Using index; Using temporary; Using filesort | 
| 1 | SIMPLE  | t2 | eq_ref | PRIMARY    | PRIMARY   | 4  | dbmodl_v18.t1.organization_id | 1 | Using where         | 
| 1 | SIMPLE  | t0 | ref | bundle_idx,shop_id_idx | shop_id_idx  | 4  | dbmodl_v18.t1.organization_id | 309 |            | 
| 1 | SIMPLE  | t3 | eq_ref | PRIMARY    | PRIMARY   | 4  | dbmodl_v18.t0.id    | 1 |            | 
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+ 

雖然確實秀「使用索引」因爲當我們只是的ID查詢:

+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+ 
| id | select_type | table | type | possible_keys   | key    | key_len | ref       | rows | Extra          | 
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+ 
| 1 | SIMPLE  | t1 | index | PRIMARY    | shop_group_id_idx | 5  | NULL       | 102 | Using index; Using temporary; Using filesort | 
| 1 | SIMPLE  | t2 | eq_ref | PRIMARY    | PRIMARY   | 4  | dbmodl_v18.t1.organization_id | 1 | Using where         | 
| 1 | SIMPLE  | t0 | ref | bundle_idx,shop_id_idx | shop_id_idx  | 4  | dbmodl_v18.t1.organization_id | 309 | Using index         | 
| 1 | SIMPLE  | t3 | eq_ref | PRIMARY    | PRIMARY   | 4  | dbmodl_v18.t0.id    | 1 |            | 
+----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+ 

奇怪的是,無論做列出正確的索引使用...但我猜它迴避了問題:

他們爲什麼不同(考慮所有其他條款是完全相同的)?這是一個爲什麼它更慢的跡象?

不幸的是,在EXPLAIN結果中,當「Extra」列爲空/ null時,MySQL文檔沒有提供太多的信息。

+0

如果可以從索引中提取列而不查找實際記錄('EXPLAIN'顯示'Using index'),那麼您可能會看到顯着的性能提升。這是怎麼回事? – eggyal

+0

它真的更快嗎?還是剛纔表格緩存在內存中?測試之間的「刷新表」和「重置查詢緩存」將爲您提供更真實的基準測試。 – invertedSpear

回答

1

比速度更重要的是,您的查詢邏輯存在缺陷。當您在WHERE子句中測試LEFT JOINed列時(除了測試NULL),您強制該連接的行爲就像它是INNER JOIN一樣。相反,你會想:

SELECT 
    a.*, b.*, c.*, x.* 
FROM 
    a 
    LEFT JOIN b ON b.a_id = a.id 
     AND b.flag = true 
    LEFT JOIN c ON c.b_id = b.id 
    LEFT JOIN x ON x.a_id = a.id 
ORDER BY 
    x.date DESC 
LIMIT 25 

我的下一個建議是要檢查所有這些.*的在你的選擇。你真的需要全部列全部表?

+1

+1'SELECT'ing *使用'JOIN'的做法很差# – Kermit

+0

有關JOIN子句的有趣說明......但奇怪的是,將條件移入JOIN子句實際上使得它更慢* ...選擇全部EXPLAIN將「a」表查詢更改爲: | 1 | SIMPLE | t0 | ALL | NULL | NULL | NULL | NULL | 45813 |使用臨時;使用filesort | (不好 - 做了一個全表掃描) 但是,有趣的是,當我切換到只選擇a.id(和上面建議的新JOIN邏輯一起)時,查詢執行的方式與2查詢方法從上面。 –

+0

另外,是的 - 謝謝你的注意。* - 同意,在SELECT子句中應該使用「*」是非常罕見的。我只是在這裏做了這些,以使我的文章更具可讀性,但在我們的代碼中,我們確實明確地選擇了我們需要的列。 –