数据库转储到文本文件,带有“行已完成”的副作用

时间:2013-10-31 01:05:49

标签: jdbc clojure

我正在尝试使用函数将数据库sql转储从select语句写入文本文件。返回的卷可能非常大,我有兴趣尽快做到这一点。

对于大的结果集,我还需要记录每个x-interval写入的总行数以及自上一个x-interval以来已写入的每秒行数。我有一个(地图)实际上在(打开)期间进行写操作,所以我相信记录行的副作用应该在那里发生。 (见代码中的评论)。

我的问题是:

  1. 如何在间隔期间写入“每秒行数”和“到目前为止的总行数”?
  2. 在将大型jdbc结果集写入文件(或命名管道,批量加载程序等)时,还有什么需要注意的吗?
  3. (map)函数周围的(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") )
    

2 个答案:

答案 0 :(得分: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会在编写结果时删除结果,因此如果您想等待足够长的时间,可以在无限大的数据上运行它。

答案 1 :(得分:1)

情侣一般提示:

草图:

(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)))))))