19

我有兩個大型表,每個大約有1億條記錄,恐怕我需要在兩個表之間執行內部連接。現在,兩張桌子都很簡單。這裏的介紹:SQL:內部連接兩個大型表

生物實體表:

  • BioEntityId(INT)
  • 名稱(nvarchar的4000,雖然這是一個矯枉過正)
  • TYPEID(INT)

臨時股東大會表(一個附配表,實際上得到的本體導入操作的):

  • EMGId(INT)
  • PID(INT)
  • 名稱(nvarchar的4000,雖然這是一個矯枉過正)
  • TYPEID(INT)
  • 上次更改時間(日期)

我需要獲取一個匹配的名稱,以便將BioEntityId與駐留在EGM表中的PId相關聯。最初,我試圖用單個內部連接完成所有事情,但查詢似乎耗時過長,並且數據庫的日誌文件(以簡單恢復模式)設法咀嚼所有可用磁盤空間(超過200 GB,當數據庫佔用18GB)並且等待兩天後查詢會失敗,如果我沒有弄錯。我設法防止日誌增長(現在只有33 MB),但查詢已經連續運行了6天,看起來不會很快停止。我在一個相當不錯的電腦上運行它(4GB內存,Core 2 Duo(E8400)3GHz,Windows Server 2008,SQL Server 2008),我注意到計算機每30秒偶爾會發生一次卡塞(給或採取)幾秒鐘。這使得它很難用於其他任何事情,這真的讓我很緊張。

現在,這裏的查詢:

SELECT EGM.Name, BioEntity.BioEntityId INTO AUX 
FROM EGM INNER JOIN BioEntity 
ON EGM.name LIKE BioEntity.Name AND EGM.TypeId = BioEntity.TypeId 

我手動設置一些指標了; EGM和BioEntity都有一個包含TypeId和Name的非聚集覆蓋索引。然而,查詢運行了五天,也沒有結束,所以我試着運行數據庫優化顧問來讓這件事情起作用。它建議刪除我的舊索引並創建統計信息和兩個聚集索引(每個表上只有一個,只包含TypeId,我覺得它很奇怪 - 或者只是簡單的愚蠢 - 但我仍然放棄它)。

已現在正在運行6天,我仍然不知道該怎麼辦...... 任何想法的傢伙?我怎樣才能讓這個更快(或者至少是有限的)?

更新: - 好吧,我已經取消了查詢,並重新啓動服務器,以獲得OS重新啓動和運行 - 我重新運行工作流程與您建議的修改,特別是裁剪爲nvarchar領域的尺寸小得多,併爲「=」交換「like」。這是要花至少兩個小時,所以我會在

更新2晚些時候發佈進一步的更新(格林尼治標準時間下午1:00時,18/11/09): - 估計執行計劃揭示了67%的成本關於表掃描,然後是33%的散列匹配。接下來是0%的並行性(這不奇怪嗎?這是我第一次使用估計的執行計劃,但這個特殊的事實剛剛解除了我的眉毛),0%的散列匹配,0%的並行性,0%的頂級,0 %表插入,最後再選擇0%。似乎索引是垃圾,正如預期的那樣,所以我將製作手動索引並丟棄糟糕的建議索引。

+1

只是好奇...爲什麼你需要100+萬行回來,你有什麼打算用這些數據做? – 2009-11-17 16:22:56

+0

4k名稱字段中存儲的最大值是多少?如果它實質上小於4k,那麼在每個表格中縮小尺寸。 – 2009-11-17 16:23:14

+0

生物信息學...:] – 2009-11-17 16:23:24

回答

6

對於龐大的加入,有時明確選擇loop join速度東西:

SELECT EGM.Name, BioEntity.BioEntityId INTO AUX 
FROM EGM 
INNER LOOP JOIN BioEntity 
    ON EGM.name LIKE BioEntity.Name AND EGM.TypeId = BioEntity.TypeId 

與往常一樣,發佈您的估計的執行計劃可以幫助我們提供更好的答案。

編輯:如果兩個輸入進行排序(他們應該是,與覆蓋索引),你可以嘗試MERGE JOIN

SELECT EGM.Name, BioEntity.BioEntityId INTO AUX 
FROM EGM 
INNER JOIN BioEntity 
    ON EGM.name LIKE BioEntity.Name AND EGM.TypeId = BioEntity.TypeId 
OPTION (MERGE JOIN) 
+1

我現在取消了查詢,讓我們看看SQL Server是否可以從死裏復活並給我們計劃... – 2009-11-17 16:29:46

+0

好吧,服務器死了,重新啓動,在夜間重新配置工作流程;現在發佈結果 – 2009-11-18 12:48:34

+0

'內循環連接'使用更少的內存和更多的CPU? – seyed 2013-02-03 20:38:48

1

1億條記錄是巨大的。我會說與一個數據庫一起工作,你需要一個專用的測試服務器。在執行這樣的查詢時使用同一臺機器做其他工作是不現實的。

你的硬件是相當能幹,但加入該大執行體面你需要更多的權力。擁有8GB的四核系統將是一個好的開始。除此之外,你必須確保你的索引設置得恰到好處。

+1

我會向我的老闆推特;)謝謝 – 2009-11-17 16:53:43

+1

大聲笑是啊告訴他StackOverflow說你還需要一個新的AlienWare筆記本電腦! – 2009-11-17 17:17:05

+0

和兩個30英寸的顯示器。這是很多數據看 – 2009-11-17 22:39:53

4

我想嘗試刪除'LIKE'操作符;因爲你似乎沒有做任何通配符匹配。

+0

不是真的不,我也嘗試了等號(「=」),但它看起來沒有前途。我會換掉它,謝謝! – 2009-11-17 16:29:10

+4

沒有通配符,LIKE應該優化成「=」。 – 2009-11-17 16:43:07

14

我不是一個SQL調優專家,但在一個VARCHAR字段加入數億行的聽起來並不像在任何數據庫系統,我知道一個好主意。

你可以嘗試在姓名字段應該得到的可能匹配到一個合理的號碼前發動機必須要看實際VARCHAR數據添加一個整數列,每個表和計算哈希值。

+0

有趣的想法。我稍後再試,謝謝。 – 2009-11-17 16:28:09

+0

CHECKSUM對此很有幫助。 – 2009-11-17 21:44:11

+0

校驗和會起作用,但根據NAME中數據的性質,您可能可以使用更快的散列算法(也許NAME在前10個字符中是唯一的,或者類似的東西)。 – 2009-11-17 22:32:35

3

按照建議,我會散列這個名字來使連接更合理。如果可能的話,我會強烈考慮在通過查找導入批次的過程中調查id的分配情況,因爲這樣可以避免以後需要進行連接(並且可能反覆不得不執行如此低效的連接)。

我看你對這個typeid的指標 - 這將極大地幫助,如果這是在所有的選擇。此外,隨着名稱的哈希添加列以同一指數:

SELECT EGM.Name 
     ,BioEntity.BioEntityId 
INTO AUX 
FROM EGM 
INNER JOIN BioEntity 
    ON EGM.TypeId = BioEntity.TypeId -- Hopefully a good index 
    AND EGM.NameHash = BioEntity.NameHash -- Should be a very selective index now 
    AND EGM.name LIKE BioEntity.Name 
+0

我會沿着這條路進一步嘗試,我現在需要探索估算計劃。謝謝:) – 2009-11-18 12:57:33

2

另一個建議我可能會提供被嘗試一次獲取數據的一個子集,而不是處理所有100 M行來調整你的查詢。這樣,您不必花費太多時間等待查看您的查詢何時完成。然後你可以考慮檢查查詢執行計劃,這也可以提供一些洞察手頭的問題。

+1

這個和正確的,最小的索引可能(可能是另一個預處理步驟)是易處理性的關鍵。 – Don 2009-11-17 17:05:55

0

既然你不問DB做任何花哨的關係操作,你可以很容易的腳本這一點。不要用大量簡單的查詢來殺死數據庫,而是嘗試導出這兩個表(可以從備份中獲得脫機副本嗎?)。

一旦你導出的表,編寫一個腳本來執行這個簡單的加入爲你。這需要大致相同的時間來執行,但不會殺死數據庫。

由於數據的大小和查詢運行的時間長度,您不會經常這樣做,因此脫機批處理過程很有意義。

對於腳本,您需要索引較大的數據集,然後遍歷較小的數據集並查找大數據集索引。它將運行O(n * m)。

1

你有沒有主鍵或索引?你可以分階段選擇它嗎?即名稱如'A%',其中名稱如'B%'等。

+0

我有PK的(EMGId和BioEntityId),並且索引發布在問題 – 2009-11-18 12:50:37

1

我手動設置了一些索引; EGM和BioEntity都有一個包含TypeId和Name的非聚集覆蓋索引。然而,查詢運行了五天,並且也沒有結束,所以我嘗試運行數據庫優化顧問來讓這件事情起作用。它建議刪除我的舊索引並創建統計信息和兩個聚集索引(每個表上只有一個,只包含TypeId,我覺得它很奇怪 - 或者只是簡單的愚蠢 - 但我仍然放棄它)。

你說你在TYPEID兩個表中做了一個聚集索引,雖然看起來你對每個表已經(BioEntityId & EGMId,分別)主鍵。您不要希望您的TypeId成爲這些表上的聚集索引。您希望BioEntityId & EGMId在磁盤上的聚集索引的順序進行聚類(將物理排序你的數據,你想上​​外鍵非聚集索引您將使用進行查找。即TYPEID。嘗試製作主鍵集羣,並且在兩個僅包含TypeId的表上添加一個非聚集索引

在我們的環境中,我們有一個表大概每個記錄10-20萬條記錄我們做了很多類似於你的查詢,在這裏我們將兩個數據集合在一個或兩個列上,添加一個索引,每個外鍵應該對你的性能有很大的幫助

請記住,有1億條記錄,這些索引將需要很多磁盤空間。不過,表現似乎是關鍵,所以應該值得。

K.斯科特有一篇不錯的文章here,它更深入地解釋了一些問題。

+0

我知道。我這樣做了,但結果並不是我所期望的。 我放棄了,因爲SQL Server數據庫優化顧問建議它;仍然認爲這是愚蠢的 – 2009-11-18 12:43:23

1

重申了幾個之前的帖子在這裏(我會投了)......

如何選擇是TYPEID?如果您的100M +行中只有5,10甚至100個不同的值,那麼索引對您不起任何作用 - 特別是因爲您無論如何都選擇了所有行。

我建議在兩個表中的CHECKSUM(Name)上創建一個列似乎很好。也許使這個持續計算列:

CREATE TABLE BioEntity 
(
    BioEntityId int 
    ,Name   nvarchar(4000) 
    ,TypeId  int 
    ,NameLookup AS checksum(Name) persisted 
) 

,然後創建像這樣的索引(我會使用羣集,但即使非聚集會幫助):

CREATE clustered INDEX IX_BioEntity__Lookup on BioEntity (NameLookup, TypeId) 

(BOL檢查,有規則以及在可能適用於您的環境的計算列上構建索引的限制。)

做了兩個表,這會提供一個非常有選擇性的指標來支持你的查詢,如果它的修訂是這樣的:

SELECT EGM.Name, BioEntity.BioEntityId INTO AUX 
FROM EGM INNER JOIN BioEntity 
ON EGM.NameLookup = BioEntity.NameLookup 
    and EGM.name = BioEntity.Name 
    and EGM.TypeId = BioEntity.TypeId 

取決於許多因素,它仍然會運行長(不只是因爲你是將多少數據複製到新表中?),但這應該少於幾天。

+0

是的,只有一百個左右的TypeId項目。 感謝您的意見,我會盡快給您一個嘗試......我的2歲MBP剛剛死了我,我去了商店:/ – 2009-11-18 15:02:48

6

也許有點偏離主題,但是: 「我注意到計算機每隔30秒偶爾會發生幾秒鐘的阻塞(給與接受)。」

此行爲對於廉價的RAID5陣列(或可能用於單個磁盤)在複製(以及您的查詢主要複製數據)千兆字節的信息時具有特徵。

關於問題的更多信息 - 你不能把你的查詢分割成更小的塊嗎?像A,B等開頭的名字或特定範圍的ID?這可以大大減少事務/鎖定開銷。

+0

+1數據分區 – 2009-11-17 22:28:00

+0

所以這得到upvoted張貼相同我已經問過的事情? – DForck42 2009-11-18 14:42:39

+0

單個磁盤,是的。 – 2009-11-18 15:12:30

1

爲什麼nvarchar?最佳做法是,如果你不需要(或者期望需要)unicode支持,只需使用varchar。如果您認爲最長的名字少於200個字符,我會將該列設置爲varchar(255)。我可以看到已經推薦給你的散列的代價很高(看起來像這個數據庫是插入密集型的)。然而,有了這麼大的尺寸,以及名稱的頻率和隨機性,在索引散列(取決於散列)或名稱的大多數情況下,索引將快速碎片化。

我會改變名稱列如上所述,並使聚簇索引TypeId,EGMId/BioentityId(任何表的代理鍵)。然後,您可以很好地加入TypeId,並且名稱上的「粗略」連接將循環較少。要查看此查詢可能運行多長時間,請嘗試使用它作爲TypeIds的一小部分,並且應該給您估計運行時間(儘管它可能會忽略緩存大小,內存大小,硬盤傳輸速率等因素)。

編輯:如果這是一個正在進行的過程,您應該在兩個表之間強制執行外鍵約束以便將來導入/轉儲。如果不在進行中,哈希可能是您最好的。

+0

不能確定,雖然它可能就足夠了 – 2009-11-18 12:49:45

5

首先,100M行連接並不是不合理或不常見。

但是,我懷疑你看到的性能差的原因可能與INTO子句有關。因此,您不僅要進行連接,還要將結果寫入新表。 你對日誌文件變得如此巨大的觀察基本上證實了這一點。

有一件事要嘗試:刪除INTO並查看它是如何執行的。如果性能是合理的,那麼爲了解決緩慢的寫入問題,您應該確保您的數據庫日誌文件位於與數據不同的物理捲上。如果不是這樣,磁頭在讀取數據並寫入日誌時會打碎(大量搜索),並且您的perf會崩潰(可能會減少到原來的1/40到1/60) )。

0

我想知道,執行時間是由加入還是通過數據傳輸。

假設您的Name列中的平均數據大小爲150個字符,實際上您將有300個字節加上每個記錄的其他列。乘以1億條記錄,您將獲得大約30GB的數據傳輸到您的客戶端。你是否運行遠程客戶端或服務器本身? 也許您會等待30GB的數據傳輸到您的客戶端...

編輯:好的,我看你插入到輔助表。數據庫恢復模式的設置是什麼?

探討在硬件方面的瓶頸,它可能會限制資源是否被讀取或寫入數據很有意思。例如,您可以開始運行Windows性能監視器並捕獲隊列的長度以讀取和寫入磁盤。

理想,你應該把數據庫日誌文件,輸入表和輸出表上單獨的物理捲來提高速度。

+0

恢復模式設置爲簡單;瞭解到困難的方法:) 關於單獨的物理卷的簡單而合乎邏輯的建議,我只使用單個硬盤。謝謝! 我發佈執行計劃的估計現在的方式 – 2009-11-18 12:48:01

0

如果哈希比賽消耗了太多的資源,然後做的,比方說,10000行批量查詢的時間,「行走」的TYPEID列。你沒有說TypeID的選擇性,但是大概它足夠有選擇性地能夠做到這麼小的批次,並且一次完全覆蓋一個或多個TypeID。您還在尋找批處理中的循環連接,因此如果仍然使用散列連接,則強制循環連接或減少批處理大小。

使用批次也將在簡單恢復模式,讓您的TRAN日誌變得非常大。即使在簡單恢復模式下,像您一樣的巨大連接也會佔用大量空間,因爲它必須保持整個事務處於打開狀態,而在進行批處理時,它可以爲每個批處理重用日誌文件,將其大小限制爲所需的最大空間一批操作。

如果你真的需要加入的名稱,那麼你可以考慮名稱轉換爲ID的一些輔助表,基本上暫時修復非規範化的設計(如果您不能永久修復)。

約校驗的想法可以很好了,但是我還沒有與打的非常多,我自己。

在任何情況下,這樣一個巨大的哈希匹配是不會作爲配料的循環連接來進行爲好。如果你能得到一個合併連接這將是真棒......

1

我會盡力解決外箱的問題,也許有一些其他的算法,可以做的工作比數據庫更好更快。當然,這一切都取決於數據的性質,但也有一些字符串搜索算法是非常快(博耶 - 穆爾,ZBox中等等),或其他數據挖掘算法(MapReduce的?)通過精心製作的數據導出它可能是可能的彎曲問題以適應更優雅更快的解決方案。此外,可以更好地並行處理問題,並且通過簡單的客戶端利用您周圍的系統的空閒週期,有框架可以幫助解決這個問題。

的這個輸出可能是因爲你可以用它來更快地從數據庫中獲取完整的數據REFID元組的列表。

這並不妨礙你從指數試驗,但如果你要等待6天的結果我想證明的資源花在探索其他可能的選擇。

我2%的

+0

嗯好主意,謝謝! – 2009-11-19 11:57:09