2009-02-04 140 views
6

我有一個MySQL 4.x的DB以下3個表之間的優化:SQL查詢:內部聯接大表

  • 主機:(300.000記錄)
    • ID(無符號整數) PRIMARY KEY
    • 名稱(VARCHAR 100)
  • 路徑:(6.000.000記錄)
    • ID(無符號整數)PRIMARY KEY
    • 名(VARCHAR 100)
  • 網址:(7.000.000記錄)
    • 主機(無符號整數)PRIMARY KEY < - - 鏈接到主機.id
    • 路徑(UNSIGNED INT)主鍵< ---鏈接到paths.id

正如您所看到的,架構非常簡單,但問題在於這些表中的數據量。

這裏是我運行查詢:

SELECT CONCAT(H.name, P.name) 
FROM hosts AS H 
INNER JOIN urls as U ON H.id = U.host 
INNER JOIN paths AS P ON U.path = P.id; 

這個查詢工作完全正常,但需要50分鐘來運行。有人對我如何加快查詢有任何想法嗎?

在此先感謝。 Nicolas

回答

1

一兩件事,我不會做CONCAT查詢。在外面做。

但是,真的,你的查詢運行緩慢,因爲你檢索了數百萬行。

5

也許你應該包括一個WHERE子句?或者你真的需要所有的數據?

1

您是否已經在連接屬性中聲明瞭一些索引?

PS:見here [無效鏈接]索引的MySQL的4.x的

+0

實際上,如果他真的想要返回所有行,索引可能沒有幫助。對錶中的每個值執行索引查找可能比全面掃描表並將其散列或合併在一起慢。 – 2009-02-04 14:25:58

+0

我看到幾百兆字節的數據。如果這一切都符合記憶 - 你是對的。但是一個合適的DBMS(我甚至猜測MySQL 4.x,因爲它足夠合適)會自動忽略現有的索引。 – Leonidas 2009-02-04 14:39:37

1

嘗試優化你的表運行查詢之前:

optimize table hosts, paths, urls; 

這可能會給您節省一些時間,尤其當行已經從表中刪除。 (有關OPTIMIZE的更多信息,請參閱here

0

concat肯定會減慢你的速度。我們可以看到一個mysql解釋的結果嗎?Documentation Link

要做的最大的事情就是儘量只拉取所需的數據。如果你可以拉出更少的記錄,這會加快你的速度。但是一個mysql解釋應該幫助我們看看是否有任何索引可以幫助。

1

我想嘗試用你想得到的數據創建一個新表。這樣做意味着你失去了一些真實的數據,但你快速獲勝。這個想法可能類似於OLAP或類似的東西嗎?

當然,您必須對此表進行更新(每天或任何)。

+0

是的,如果他一直不需要最新的數據,那麼「物化視圖」是值得推薦的。 – Leonidas 2009-02-04 14:05:44

4

這對我來說就像是一個過度熱心地使用代理鍵會減慢你的情況。如果表爲:

  • 主機:

    • 名稱(VARCHAR 100)PRIMARY KEY
  • 路徑:

    • 名稱(VARCHAR 100)PRIMARY KEY
  • 網址:

    • 主機(VARCHAR 100)PRIMARY KEY < ---鏈接hosts.name
    • 路徑(VARCHAR 100)PRIMARY KEY < ---鏈接paths.name

那麼你的查詢就不需要加入可言:

SELECT CONCAT(U.host, U.path) FROM urls U; 

真的,表URL將佔用更多的磁盤空間 - 但是這很重要嗎?

編輯:關於第二個想法,無論如何,PATHS表有什麼意義?不同主機共享相同路徑的頻率如何?

爲什麼不:

  • 主機:

    • 名(VARCHAR 100)PRIMARY KEY
  • 網址:

    • 主機(VARCHAR 100)PRIMARY鑰匙< ---與主機的鏈接。命名
    • 路徑(VARCHAR 100)PRIMARY KEY < ---沒有聯繫任何地方

EDIT2:或者,如果你真的需要主機上的代理鍵:

  • hosts:

    • ID整數PRIMARY KEY
    • 名(VARCHAR 100)
  • 網址:

    • 主機整數PRIMARY KEY < ---鏈接hosts.name
    • 路徑(VARCHAR 100)PRIMARY KEY < ---無法連接到任何地方

    SELECT CONCAT(H.name,U.path)FROM urls U 加入主機H ON H.id = U.host;

+0

我剛剛添加了一個與「第二次思考」部分相同的答案。 – 2009-02-04 14:37:03

0

我知道你想要一個完整的url列表 - 這是700萬條記錄。 也許as sugested by Mitch你應該考慮使用WHERE子句來過濾你的結果。 也許定時主要涉及延遲顯示記錄

檢查時間此查詢

select count(*) 
FROM hosts AS H 
INNER JOIN urls as U ON H.id = U.host 
INNER JOIN paths AS P ON U.path = P.id 

如果這仍是緩慢的,我會去檢查時間從網址

SELECT COUNT(*)

然後

select count(*) 
from urls u 
inner join hosts h on u.host = h.id 

然後

select count(*) 
from urls u 
inner join hosts h on u.host = h.id 
inner join paths p on u.path = p.id 

只是爲了找到減緩

有時也被重新排序查詢,有助於

SELECT CONCAT(u.host, u.path) 
from urls u 
inner join hosts h on u.host = h.id 
inner join paths p on u.path = p.id 
0

我不能肯定關於MySQL說,但我知道,在SQL Server中的主鍵自動創建一個索引,但外鍵不會。確保檢查外鍵字段上是否有索引。

1

我不是MySQL的專家,但它看起來像MySQL的主鍵集羣 - 你要確保這是你的主鍵的情況;聚簇索引肯定有助於加快速度。

有一點,雖然 - 我不相信你可以在任何表上有兩個「主」鍵;出於這個原因,你的網站表格看起來相當可疑。最重要的是,您應該絕對確保urls表中的這兩列被索引到索引 - 每一個數字索引都應該沒問題 - 因爲您正在加入它們,因此DBMS需要知道如何快速找到它們;這可能是你的情況發生了什麼。如果你全桌面掃描了很多行,那麼是的,你可能會坐在那裏很長一段時間,而服務器試圖找到你要求的所有東西。

我也建議從select語句中刪除CONCAT函數,並查看它是如何影響您的結果的。如果這不是一個促成因素,我會很驚訝。只要檢索兩列並處理後續的連接,並看看如何。

最後,你有沒有想出瓶頸在哪裏?只要加入三個數百萬行的表格應該不會佔用太多時間(我希望可能會花費大約一秒鐘的時間,只是看着你的表格和查詢),只要表格已正確編制索引。但是,如果您將這些行通過緩慢或已掛接的NIC推送到內存不足的應用程序服務器等,則緩慢可能與查詢無關,而是與查詢後發生的事情無關。不管這些行的發現需要多長時間,七百萬行都是相當多的數據要彙編和移動。嘗試只選擇一行而不是全部七百萬,然後看看相反的情況。如果這很快,那麼問題不在於查詢,而是結果集。

2

總的來說,最好的建議是跟蹤和分析,看看真正花費時間。但是,這裏是我關於具體事情的看法。 (1)我想說,你要確保索引不用於執行此查詢。由於您沒有過濾條件,因此全面掃描所有表並使用排序合併或散列操作將它們連接在一起應該更有效。

(2)字符串連接肯定需要一些時間,但我不明白爲什麼人們建議刪除它。你大概需要在另一段代碼中進行連接,在這段代碼中它仍然需要大約相同的時間(除非由於某種原因MySQL的字符串連接特別慢)。 (3)從服務器到客戶端的數據傳輸可能花費大量時間,很可能比服務器需要獲取數據的時間長。如果您有工具來追蹤這類事情,請使用它們。如果您可以增加客戶端中的讀取數組大小,請嘗試使用不同的大小(例如,在JDBC中使用Statement.setFetchSize())。即使客戶端和服務器位於同一主機上,這也可能很重要。

1

由於您的結果集會返回所有數據,因此幾乎沒有任何優化可以完成。您正在掃描整個表格,然後加入其他具有索引的表格。

PrimaryKeys是否被羣集?這可以確保數據以索引順序存儲在磁盤上,因此避免在磁盤的不同部分之間跳動。

此外,您可以將數據分佈在多個磁盤上。如果您在PRIMARY和PATTH/HOSTS上的URL位於SECONDARY,那麼您將從驅動器獲得更高的吞吐量。

1

您需要查看您的服務器配置。 MySQL的默認內存參數將削弱大小的表的性能。如果您使用的是默認值,則需要至少提高key_buffer_sizejoin_buffer_size至少4倍,也許更多。查看文檔;還有其他的內存參數可以調整。

MySQL有一個有趣的性能怪癖,如果你的表超過一定的大小與查詢將返回大部分數據,性能進入廁所。不幸的是,它無法告訴你什麼時候達到了這個門檻。儘管如此,它看起來像你一樣。

0

由於我不是一個大的MySQL粉絲,我會問你是否嘗試過PostgreSQL。在該數據庫中,您需要確保您的work_mem設置非常高,但可以使用SET work_mem = 64MB爲每個數據庫連接進行設置。

另一個建議是研究使用重複路徑條目。有許多共享路徑的URL。

另一件可能或可能不會幫助的事情是使用固定長度的文本字段而不是varchars。它曾經造成速度差異,但我不確定當前的數據庫引擎。

如果您確實使用PostgreSQL,它會讓您使用JOIN USING,但即使在MySQL上我也更喜歡它:在每個表中爲您的id字段命名相同。而不是在主機和主機在URL中的id,將它命名爲host_id這兩個地方。

現在更多評論。 :) 當您選擇一小組行時,可能來自同一個域的每個URL,您在此處的數據佈局非常有用。如果您的查詢經常需要對存儲在那裏的其他數據執行urls表的順序掃描,它也可以幫助lot,因爲掃描可以跳過大文本字段(除非因爲您的數據庫存儲文本通過反正指向一個鏈接表)。

但是,如果您幾乎總是選擇所有域和路徑數據,那麼將其存儲在一個表中更有意義。