2011-12-29 23 views
1

我們有一個定期讀取CSV文件並將記錄插入Oracle 11g數據庫的ColdFusion 9腳本。 CSV文件大約有50列,其中8個由CF使用(CSV格式不能修改)。該CFM的一般流程是:讀取用於數據庫導入的csv文件時ColdFusion內存尖峯化

  1. 讀取使用該變量作爲與CHR作爲分隔符的列表屬性(13)文件導入可變
  2. CFLOOP
  3. 呼叫Oracle存儲過程中插入來自各個值使用ListGetAt

存儲過程文件執行下列操作:

  1. 插入具有2網絡連接的記錄視場爲表1
  2. 插入8個領域(包括表1的主鍵)爲表2
  3. 沒有返回值

這成功地運行在大多數情況下,閱讀數以百計的記錄400頁KB的文件記錄只需幾秒鐘。但是,偶爾我們會得到一個大容量,並且最終創建一個13k創紀錄的5MB文件。當我們嘗試處理一個這樣大的文件時,我觀察到JVM的內存使用量在10-15秒內從90MB增加到680MB左右,之後CF服務器監視器停止響應(CF也如此),迫使我們重新啓動服務。日誌報告JVM內存不足錯誤:

"Error","qtp4795249-38798","12/28/11","16:29:20",,"GC overhead limit exceeded" java.lang.OutOfMemoryError: GC overhead limit exceeded

我們的JVM堆大小目前是768MB。我還沒有嘗試過增加它,因爲即使這樣做確實解決了這個問題,它將來也無法保護我們,而服務器的其他正常負載並不需要那麼多。而且,我對使用JVM設置需要重啓才能在生產環境中生效的做法感到猶豫不決。

這很難測試,因爲導入過程運行正常,幾乎沒有任何明顯的內存負載在我的本地開發機器和我們的QA盒子上,但是這兩者與數據庫的連接速度要慢得多,需要10-15分鐘去完成。

我會很感激任何想法,特別是關於內存的去向。我無法弄清楚5MB數據如何變成700MB的數據。我們打開了調試信息,但調用腳本的IP地址不在調試列表中,我使用了一個cfsetting標籤來關閉此頁面的調試功能。以前,有一個步驟1.5將CSV數據轉換爲ColdFusion查詢,但我爲了提高效率而將其刪除。兩種方式都會導致錯誤。

+0

感謝所有的迴應。由於客戶發生的月末和年終處理,以及他們昨天對CF服務器持續30秒的響應,我們將等待一週左右,然後嘗試實施或測試任何這些建議。然後我會更新這個問題。 – Nicholas 2011-12-30 18:49:05

回答

1

你有沒有考慮直接進口到數據庫?對於MySQL,這是LOAD DATA INFILE,對於SQL Server它是BULK INSERT。如果你需要一些額外的處理,比可能的方法將數據加載到臨時表,然後用CFML處理它,這可以很容易地批量進行處理。

+0

謝謝你的建議。我們確實考慮過這個問題,但是我們的數據庫管理員目前處於非常好的狀態,管理層希望在嘗試從頭開始編寫新的數據庫導入之前嘗試在ColdFusion中快速解決此問題。 – Nicholas 2011-12-29 20:40:05

1

在開始處理文件之前,不要將整個文件讀入內存,而是一次讀取一行文件內容。本納德爾(當然)有一個很好的博客文章討論這個技術:

Reading In File Data One Line At A Time Using ColdFusion's CFLoop Tag Or Java's LineNumberReader

另請參見livedocs for cfloop,特別是文件attribtue:

+0

這些都是好想法。我會研究這些並報告回來。謝謝。 – Nicholas 2011-12-29 20:40:25

+0

本的一些好東西,但讓某人離開這個網站得到他們的答案不是很好。你至少可以解釋本說什麼嗎? – ale 2011-12-30 13:44:00

+0

我的建議沒有比cfloop file =「foo.txt」更多的了。我還應該包括什麼? – 2011-12-30 15:59:14

0

我們有一個CF應用程序,它導入房地產MLS列表並遇到類似的問題。我們所使用的核心文件正在推送100MB,並且讀取它並立即進行循環創建了許多問題。我們結束了幾件事:

  1. 將文件拆分成塊。導入過程使用cygwin的split.exe實用程序將文件拆分爲4,000行塊。然後,我們使用CFDIRECTORY獲取塊的列表並逐個處理它們。

  2. 對於每個塊,我們將其讀入,然後將文件內容拆分爲一個數組(使用帶有chr(13)作爲分隔符的listToArray())。

  3. 我們從1循環到arrayLen(chunkArray),而不是直接遍歷文件內容。這比速度更快。在那個循環中,我們也將每一行分解成一個數組。我們發現這樣做並以thisRow [i](其中i是文件中的列號)訪問值比反覆調用listGetAt()快得多。我們正在導入的文件有90多列。

  4. 我們增加了JVM內存容量。我們的服務器非常繁忙,因此增加了一些開銷。我們最終推動JVM達到32位服務器(約2GB)的高度,以便在需要時提供內存。

0

也看看沒有循環查詢。執行查詢花費的最多時間只是建立數據庫連接。

任何時候我必須從一個文件中這樣做多次插入,我創建每個SQL插入語句並將它們保存在一個由分號分隔的變量中。然後,我每100次陳述就一次執行所有這些陳述。

我不得不通過這樣做來重寫另一位程序員的某些程序,並且能夠將加工時間減少90%。這是在版本6中,所以連接共享可能會改善這一點。

+0

這也是我的第一個想法。不幸的是,數據庫連接在單個查詢中不接受多個語句,我不相信這是我可以改變的。 – Nicholas 2012-01-03 13:37:31

+0

它並不是多個查詢之一,它只是一個接一個地運行它們,就像你在SQL PLUS中運行它們一樣,都用分號分隔。我不太確定數據庫如何限制這一點。 – Limey 2012-01-03 19:17:52

+0

您是否問過在一個用分號隔開的cfquery塊中放置幾個​​insert語句?這可以並且受用於數據庫連接的驅動程序的限制。我們正在使用內置的Oracle JDBC驅動程序,我不確定如何以這種方式進行配置。我知道在我的家庭服務器上使用MySQL時,在驅動程序配置中有一個複選框允許/禁止這個;在我想出來之前,讓我頭痛了好幾天。 :P – Nicholas 2012-01-04 13:20:01