2014-11-03 107 views
2

我有4臺MySQL數據庫(不止於此着呢,但只有這4個相關的問題),讓我們稱之爲A,B,C和D.這裏的架構:奇怪的MySQL連接行爲

CREATE TABLE A 
(
    pKey INT NOT NULL AUTO_INCREMENT, 
    name NVARCHAR(50), 
    PRIMARY KEY(pKey), 
    UNIQUE INDEX(name) 
); 

CREATE TABLE C 
(
    pKey INT NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY(pKey) 
); 

CREATE TABLE B 
(
    pKey INT NOT NULL AUTO_INCREMENT, 
    aKey INT NOT NULL, 
    cKey INT NOT NULL, 
    PRIMARY KEY(pKey), 
    UNIQUE INDEX UniqueKey (aKey, cKey), 
    FOREIGN KEY(aKey) REFERENCES A(pKey), 
    FOREIGN KEY(cKey) REFERENCES C(pKey) 
); 

CREATE TABLE D 
(
    pKey INT NOT NULL AUTO_INCREMENT, 
    cKey INT NOT NULL, 
    PRIMARY KEY(pKey), 
    INDEX(cKey), 
    FOREIGN KEY(cKey) REFERENCES C(pKey) 
); 

我運行下面的查詢:

SELECT 
    --stuff... 
FROM A 
INNER JOIN B 
    ON A.pKey=B.aKey 
INNER JOIN C 
    ON B.cKey=C.pKey 
INNER JOIN D 
    ON D.cKey=C.pKey 
WHERE 
    A.name=parameter_1; 

麻煩的是,這是一臺服務器上運行一個大型數據庫,大多數表有100K +的記錄,它的情況並不少見一張桌子打破1000萬條記錄。一張桌子有超過2億條記錄。

撇開與MySQL的任何問題和架構(我卡與兩個),我越來越有當我使用這個查詢說明上面的查詢一些奇怪的行爲。由於這種行爲,我有幾個問題。我會先顯示奇怪的行爲。

如果我只是在MySql中解釋上面的查詢,那麼我會在EXPLAIN輸出的參考列中得到參考。但是,我需要將此查詢作爲較大查詢的子查詢來運行。 EXPLAINning較大查詢給了我這樣的事情以上查詢(這只是一個對應表在此查詢的較大查詢行):

+----+-------------+-------+-------+---------------------+---------+---------+-----------------+-------+--------------------------+ 
| id | select_type | table | type | possible_keys  | key  | key_len | ref    | rows | Extra     | 
+----+-------------+-------+-------+---------------------+---------+---------+-----------------+-------+--------------------------+ 
| 1 | SIMPLE  | A  | const | PRIMARY,key1,key2 | key1 | 38  |     |  1 |       | 
| 1 | SIMPLE  | D  | index | key3    | key3 | 12  | NULL   | 73868 | Using index    | 
| 1 | SIMPLE  | C  | index | PRIMARY,key3  | PRIMARY | 8  | NULL   |  1 |       | 
| 1 | SIMPLE  | B  | ref | key4,UniqueKey,key6 | key4 | 12  | const,DB.D.key3 |  1 | Using where; Using index | 
+----+-------------+-------+-------+---------------------+---------+---------+-----------------+-------+--------------------------+ 

MySQL是做兩點索引掃描和一個REF鍵入連接。如果我使用索引提示,我可以略微改善這一點,但只是略有改善。我之前說過,這個查詢是作爲一個子查詢運行的。下面是其他查詢的格式:

SELECT 
    --stuff 
FROM 
(
    --sub-query1 
) a 
INNER JOIN 
(
    --the query I have a question about 
) b 
ON a.c1=b.c2 

優化完全忽略表C中有利於做加入的兩個外鍵列,B.cKey = D.cKey的。 所以,這裏的問題1)爲什麼優化器忽略這樣的表C?

接下來,即使我使用索引提示,並且它忽略了表C,它仍然執行索引掃描以加入B和D,儘管有適當的索引。 爲什麼?

在上面的解釋中,它表明在表D中有73,868行,在這個問題時實際上有73,568行。其中一個查詢表(在這個問題中沒有顯示)有大約1億行,所以優化這個是相當重要的。對於完整查詢,行列的乘積約爲2.37E42。是的,我已經考慮了減少查詢中表的數量的方法;我需要獲取的信息需要每個我正在訪問的表,並且我無法更改數據庫的體系結構。

最後,我可以在這裏改變的唯一的東西是查詢和索引/約束。由於這是一個先前存在的系統,因此我一直堅持一切。 我還有其他方法可以更好地優化此操作嗎?

謝謝!

編輯:我定爲超級查詢的格式。

回答

0

你可以添加一個索引,這個模式?

如果是的話,我建議你複合索引(name, pKey)添加到您的表A。不要費心去設置一個獨特的約束;你已經用你的其他索引處理過了。該化合物將允許您的選擇標準A.name=parameter_1和您的聯接滿足一個索引掃描。

您使用單列表C只是簡單地刪除不在該表中的結果集行。除非您的查詢有可怕的性能問題,否則我不擔心它從EXPLAIN中丟失。

通常,在處理這些多路連接操作時,應該嘗試使用複合覆蓋索引來幫助您的性能。你可以閱讀這種索引。

+0

我添加了你建議的索引,並沒有區別。表C實際上具有更多列,即一個主鍵,並且我從這些列中選擇值。由於我不認爲這些附加列與JOIN操作有關,所以我沒有包含它們。 – wizard07KSU 2014-11-04 00:32:08