2

select ステートメントからデータベースの SQL ダンプをテキスト ファイルに書き込む関数を作成しようとしています。返されるボリュームは非常に大きくなる可能性があるため、できるだけ早くこれを行うことに関心があります。

大きな結果セットでは、書き込まれた行の総数と、最後の x 間隔以降に書き込まれた 1 秒あたりの行数を x 間隔ごとにログに記録する必要もあります。私は (with-open ) 中に実際に書き込みを行っている (map ) を持っているので、完了した行をログに記録するという副作用がそこで発生するはずです。(コード内のコメントを参照してください)。

私の質問は次のとおりです。

  1. 間隔中の「1 秒あたりの行数」と「これまでの合計行数」を書き込むにはどうすればよいですか?
  2. 大きな jdbc 結果セットをファイル (または名前付きパイプ、バルクローダーなど) に書き込む際に留意したい追加事項はありますか?
  3. (map ) 関数の周りの (doall ) はすべての結果を取得しますか?
  4. オプションで幅固定は可能でしょうか?名前付きパイプからバルクローダーへのほうが速いと思います。トレードオフは、ダウンストリーム解析のための CPU 使用率の代わりにディスク i/o になります。ただし、これには、返された結果セットのイントロスペクションが必要になる場合があります (.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") )
    
4

2 に答える 2

1

いくつかの一般的なヒント:

  • setFetchSizeJDBC レベルでは、Clojure に到達する前に、結果セット全体を RAM にロードすることを避けるために を使用する必要がある場合があります。SQL Server JDBC ドライバーで Statement.setFetchSize(nSize) メソッドが実際に行うことを参照してください。
  • clojure.java.jdbc が実際にレイジー seq を返していることを確認してください (おそらくそうですか?)。そうでない場合は、resultset-seqを検討してください。
  • doall実際、すべてをRAMに強制します。doseq代わりに試す
  • atomを使用して、書き込まれる行数を保持することを検討してください。これを使用して、これまでの行などを書き込むことができます。

スケッチ:

(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)))))))
于 2013-10-31T01:50:10.907 に答える
1

進捗状況報告のための慣用的なクロージュアへの私の答えから、いくらかの使用を得ることができますか?

具体的にあなたの状況に

1)匿名関数の2番目の引数としてマップにインデックスを追加できます。次に、マッピングしている関数でインデックスを見て、書き込んでいる行を確認します。アトムの更新に使用できます。

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代わりに使用することを最も確実に望んでいます。結果が書き込まれると結果がドロップされるため、十分に長く待ちたい場合は、無限に大きなデータで実行できます。doalldorun

于 2013-10-31T01:42:49.547 に答える