2013-10-31 42 views
2

我想要一個函數從select語句寫入數據庫sql轉儲到文本文件。返回的數量可能非常大,我有興趣儘快完成此操作。數據庫轉儲到「行完成」的副作用文本文件

對於大的結果集,我還需要記錄每個x-interval自從上一次x-interval以來寫入的總行數和每秒寫入的行數。我有一個(地圖),實際上是在一個(with-open)期間進行寫操作,所以我相信記錄行的副作用應該在那裏發生。 (見代碼註釋)。

我的問題是:

  1. 我怎樣寫的時間間隔內「行每秒」和「總行到目前爲止」?
  2. 在將大型jdbc結果集寫入文件(或命名管道,批量加載程序等)時,是否還有其他要記住的內容?
  3. (地圖)函數週圍的(doall)是否獲取所有結果...使其非懶惰並且可能需要內存密集型?
  4. 固定寬度可以作爲選項嗎?我相信對於命名管道來說散裝加載器會更快。權衡將在磁盤I/O上代替CPU利用率進行下游解析。然而,這可能需要對返回的結果集內省(與.getMetaData?)

    (ns metadata.db.table-dump 
        [:use 
        [clojure.pprint] 
        [metadata.db.connections] 
        [metadata.db.metadata] 
        [clojure.string :only (join)] 
        [taoensso.timbre :only (debug info warn error set-config!)] 
        ] 
        [:require 
        [clojure.java.io  :as io ] 
        [clojure.java.jdbc  :as j ]  
        [clojure.java.jdbc.sql :as sql]  
        ] 
    ) 
    
    (set-config! [:appenders :spit :enabled?] true) 
    (set-config! [:shared-appender-config :spit-filename] "log.log") 
    
    (let [ 
         field-delim "\t" 
         row-delim  "\n" 
         report-seconds 10 
         sql   "select * from comcast_lineup " 
         joiner   (fn [v] (str (join field-delim v) row-delim)) 
         results  (rest (j/query local-postgres [sql ] :as-arrays? true :row-fn joiner)) 
         ] 
        (with-open [wrtr (io/writer "test.txt")] 
         (doall 
          (map #(.write wrtr %) 
           ; Somehow in here i want to log with (info) rows written so 
           ; far, and "rows per second" every 10 seconds. 
           results)) 
        ) (info "Completed write")) 
    

回答

1

夫婦一般提示:

  • 在JDBC級別,你可能需要使用setFetchSize以避免整個結果集加載到RAM之前,甚至已經開始Clojure的。見What does Statement.setFetchSize(nSize) method really do in SQL Server JDBC driver?
  • 確保clojure.java.jdbc真的返回一個懶惰的序列(這大概是?) - 如果不是,考慮resultset-seq
  • doall確實會迫使整個事情是在RAM中;嘗試使用doseq而不是
  • 考慮使用​​來保持行數的計數;你可以使用它來寫行至此等等。

素描:

(let [ .. your stuff .. 
     start (System/currentTimeMillis) 
     row-count (atom 0)] 
    (with-open [^java.io.Writer wrtr (io/writer "test.txt")] 
    (doseq [row results] 
     (.write wrtr row) 
     (swap! row-count inc) 
     (when (zero? (mod @row-count 10000)) 
     (println (format "written %d rows" @row-count)) 
     (println (format "rows/s %.2f" (rate-calc-here))))))) 
+1

好東西,謝謝。公認。 – joefromct

1

你可能會得到一些使用了我的答案來Idiomatic clojure for progress reporting?

您的情況專門

1 )您可以將一個索引作爲匿名函數的第二個參數添加到您的映射中,然後在您映射的函數中查看索引以查看您正在編寫的行。可用於更新原子。然後

user> (def stats (atom {})) 
#'user/stats 
user> (let [start-time (. (java.util.Date.) getTime)] 
     (dorun (map (fn [line index] 
         (println line) ; write to log file here 
         (reset! stats [{:lines index 
             :start start-time 
             :end (. (java.util.Date.) getTime)}])) 
        ["line1" "line2" "line3"] 
        (rest (range))))) 
line1 
line2 
line3 
nil 
user> @stats 
[{:lines 3, :start 1383183600216, :end 1383183600217}] 
user> 

stats內容可打印/記錄每隔幾秒鐘更新UI

3)你肯定要使用dorun代替doall因爲你懷疑這會耗盡內存在足夠大的數據集上。 dorun在寫入結果時會丟失結果,因此如果要等待足夠長的時間,您可以在無限大的數據上運行結果。

+0

你可以使用[圖索引(http://richhickey.github.io/clojure/clojure.core-api.html#clojure.core/map-indexed )而不是'map'。 – xsc

+0

當我進入酒吧時,這會很有幫助...暫時我只是將一個比率/浮點數推送到日誌文件中。謝謝。 – joefromct

相關問題