2012-01-20 28 views
4

的Windows Server 2008 R2企業版,SQL Server 2008中X64,SP3,開發版同時SQL批量插入使用錯誤文件選項

我建立和動態執行(通過sp_executesql的)一個BULK INSERT命令時產生的故障。一般形式爲:

BULK INSERT #HeaderRowCheck 
from "\\Server\Share\Develop\PKelley\StressTesting\101\DataSet.csv" 
with 
(
    lastrow = 1 
    ,rowterminator = '\n' 
    ,tablock 
    ,maxerrors = 0 
    ,errorfile = 'C:\SQL_Packages\TempFiles\#HeaderRowCheck_257626FB-A5CD-41B8-B862-FAF8C591C7A9.log' 
) 

(錯誤文件名是基於配置的本地文件夾,該表被加載,併爲每批量插入運行新生成一個GUID - 這是包裹在它自己的存儲過程的子程序)

一個外部進程(即SQL Agent,現在是一個WCF服務)啓動DTEXEC,啓動一個SSIS包,該包調用數據庫中循環訪問存儲過程的數據庫,構建查詢併爲每個數據庫運行該查詢。最多可以同時從/到一個給定的數據庫運行四個加載,並且SQL實例上的多個數據庫可以同時運行這個數據庫 - 儘管從歷史上看,數據量一直很低,而且我們通常只有一個一次運行這個實例。我們做了很多事情,並且它在兩年多的時間裏完美無缺地工作 - 安全性得到了正確配置,存在必要的文件和文件夾,這一切都是平常的。 (運氣?我喜歡不認爲)

我們現在正在預測一些嚴重的工作量,所以我們正在進行一些壓力測試,其中我會發起8次運行,每次運行有四個進程,其中一組四個並逐個處理要加載的文件(即,最多可同時執行32個批量插入,就像我說的壓力測試一樣)。低並注意,啓動時,一個或多個文件在執行過程中會失敗,出現錯誤消息,如:

Error #4861 encountered while loading header information from file "DataSet.csv": Cannot bulk load because the file "C:\SQL_Packages\TempFiles\#HeaderRowCheck_D0070742-76A5-4175-A1A7-16494103EF25.log" could not be opened. Operating system error code 80(The file exists.).

從運行到運行時,用於同一個文件,數據集,或者點在-整體處理不會發生錯誤。

表面上,它聽起來像兩個進程試圖訪問相同的錯誤文件,這意味着他們獨立地生成相同的GUID(!)。我的理解是,這應該是幾乎不可能的。另一個理論是,很多事情正在同時進行(可能多達32個同時執行BULK INSERT命令),SQL和/或操作系統在某種程度上變得困惑(我是DBA,而不是網絡管理員)。我可以做一個解決方法,建立我的try-catch塊來檢查錯誤4861並重試三次,但我寧願避免這樣的混亂。

我已經拋棄了一個例程,它在使用之前將錯誤文件的名稱(使用guid)記錄到表中。經過多次運行和幾次失敗後,我發現(a)失敗的文件+ guid正在我的表中記錄,(b)沒有重複的guid被記錄。

任何人都知道可能會發生什麼?

菲利普

回答

6

我打開了與微軟技術支持的情況下,經過背部和往復,普拉迪普M.M.不小的(SQL Server技術支持負責人)全力以赴。

一般過程:讀取文件夾中的文件列表,並逐個在這些文件上執行一系列批量插入(首先讀取第一行,我們解析列,然後讀取數據來自第二+行)。所有批量插入都使用「ErrorFile」選項,以便在用戶數據格式錯誤時向用戶提供我們可以提供的信息。進程已運行3年以上,但在最近的壓力測試條件下(最多由單個SQL Server實例執行8次同時運行,所有文件格式正確),我們得到了上面列出的錯誤。

我們最初雖然在生成GUID時出錯,因爲那個「已經打開」的錯誤,但是這個想法最終被拋棄了 - 如果newid()不能正常工作,更多的人會有很多更嚴重的問題。

按普拉迪普,這裏是如何批量插入工作的循序漸進的過程:

  1. BULK INSERT命令被提交,並解析其語法錯誤
  2. 然後BULK INSERT命令編譯生成執行 爲同一
  3. 在編制階段計劃,如果在查詢,如果我們指定 錯誤文件參數,那麼我們將創建ErrorFile.log和 ErrorFile.Error.Txt到指定的文件夾位置(重要 一點需要明白的是該文件的大小將0KB)
  4. 的文件一旦創建完成,我們刪除使用 窗口API我們進入執行階段這兩個文件調用
  5. 一旦執行計劃已準備就緒 和嘗試執行大容量插入命令作爲其中的一部分,我們將 重新創建ErrorFile.log和ErrorFile.Error.Txt到文件夾 指定的位置(由於每本書聯機文檔錯誤 文件不應該在這裏地點,否則我們將失敗我們的 執行http://msdn.microsoft.com/en-us/library/ms188365.aspx
  6. 一旦執行完成,如果其他e是 中的任何錯誤批量插入各自的錯誤記錄到錯誤文件 創建的如果沒有錯誤,這2個文件將被刪除。

失敗運行期間運行ProcMon(進程監視器)顯示ErrorFile已成功創建並在步驟3中打開,但未在步驟4中關閉,導致第5步生成我們所看到的錯誤。 (對於成功運行,文件已按預期創建並關閉。)

ProcMon的進一步分析表明,運行CMD.EXE的另一個進程在批量插入嘗試後對文件發出「關閉句柄」操作。我們使用涉及xp_cmdshell的例程來檢索要處理的文件列表,這將是CMD.EXE進程的原因。這裏是踢球者:

...有一些商業邏輯啓動CMD.EXE SQL Server內部,因爲CMD.EXE是一個子進程,它繼承了由父進程打開的所有句柄(所以這可能是某種時機問題在CMD.EXE中存放處理文件時處於打開狀態,並且所有這些文件的處理都由CMD.EXE繼承,無法刪除並且只能在CMD.EXE銷燬後才能釋放)

就是這樣。單次運行永遠不會遇到此問題,因爲它的xp_cmdshell調用在批量插入發出之前完成。但是,隨着並行運行,特別是有許多平行運行(我只打了問題,5個或更多正在進行),發生了計時問題使得:

  1. 一個SSIS包執行和調用存儲過程 在內部使用XP_CMDSHELL並啓動CMD。EXE枚舉 文件
  2. 相同的連接到SQL服務器完成該文件列舉 ,然後開始批量插入活動,它是在編譯的BULK INSERT命令
  3. 按我們做批量插入的設計 階段在編譯階段創建錯誤文件 ,然後編譯 階段完成
  4. 同時另一SSIS包被執行後,將其刪除,它調用 內部使用XP_CMDSHELL並啓動 CMD.EXE枚舉所有的存儲過程文件
  5. CMD.EXE是一個子進程,已在父 下啓動進程SQLServr.exe,所以默認情況下它繼承了由SQLServr.exe創建的所有句柄 (因此,此進程獲取所有已處理的ERRORFILE的 句柄在第一 連接BULK INSERT)
  6. 現在,在第一個連接編譯階段已經過去, 因此我們正試圖刪除期間,我們不得不關閉 所有的處理文件創建,我們看到CMD。 EXE持有 文件的句柄,它仍然處於打開狀態,因此我們無法刪除該文件。所以 沒有刪除我們移動到執行階段的文件,並在 執行階段,我們試圖創建一個新的ERRORFILE與 同名,但由於文件已經存在,我們失敗,錯​​誤 「操作系統錯誤代碼80 (該文件存在)。「

我的短期解決方法是(1)實現重試循環,生成新的ErrorFile名稱並在放棄之前嘗試新的批量插入,最多三次,以及( 2)在我們的夜間過程中建立另一個例程以刪除在我們的「ErrorFile文件夾」中找到的所有文件。

長期修復是修改我們的代碼,不通過xp_cmdshell列出文件。這似乎是可行的,因爲整個ETL過程都是由SSIS包裹包裝和管理的;或者,CLR例程可以被構建和運行。目前,考慮到我們預期的工作負載,解決方法已經足夠了(特別是考慮到我們剛纔正在處理的所有其他事情),所以在我們實施最終決策之前固定。

發表於後代,萬一它發生在你身上!

+0

+1個棘手問題 – Omar