9

データベースから数百万行を読み取り、テキスト ファイルに書き込もうとしています。

これは、副作用のあるテキスト ファイルへの質問データベース ダンプの続きです。

私の問題は、プログラムが完了するまでログが記録されないことです。遅延処理を行っていないことを示すもう 1 つの指標は、プログラムが終了するまでテキスト ファイルがまったく書き込まれないことです。

IRC のヒントに基づいて、私の問題はコードの領域に関係していて:result-set-fn、デフォルトになっている可能性が高いようです。doallclojure.java.jdbc/query

これを関数に置き換えようとしましたforが、結果セット全体をメモリに取り込むため、メモリ消費量が多いことがわかりました。

:result-set-fnのようにすべてを引っ張らないようにするにはどうすればよいdoallですか? -main実行が終了したら すべてをダンプするのではなく、プログラムの実行中にログ ファイルを段階的に書き込むにはどうすればよいですか?

    (let [ 
          db-spec              local-postgres
          sql                  "select * from public.f_5500_sf "
          log-report-interval  1000
          fetch-size           100
          field-delim          "\t"                                                                  
          row-delim            "\n"                                                                  
          db-connection        (doto ( j/get-connection db-spec) (.setAutoCommit false)) 
          statement            (j/prepare-statement db-connection sql :fetch-size fetch-size ) 
          joiner               (fn [v] (str (join field-delim v ) row-delim ) )                      
          start                (System/currentTimeMillis)                                            
          rate-calc            (fn [r] (float (/ r (/ ( - (System/currentTimeMillis) start) 100))))  
          row-count            (atom 0)                                                              
          result-set-fn        (fn [rs] (lazy-seq rs))
          lazy-results         (rest (j/query db-connection [statement] :as-arrays? true :row-fn joiner :result-set-fn result-set-fn)) 
          ]; }}}
      (.setAutoCommit db-connection false)
      (info "Started dbdump session...")    
      (with-open [^java.io.Writer wrtr (io/writer "output.txt")]
        (info "Running query...")    
        (doseq [row lazy-results] 
          (.write wrtr row)
          ))  
        (info (format "Completed write with %d rows"   @row-count))
      )
4

3 に答える 3

8

project.clj 依存関係のリストにclojure.java.jdbc入れることで、最近の修正を行いました。これは、ここで説明されている機能を[org.clojure/java.jdbc "0.3.0-beta1"]強化/修正します。:as-arrays? trueclojure.java.jdbc/query

これは多少役に立ったと思いますが、:result-set-fntoをオーバーライドできた可能性がありますvec

コアの問題は、すべての行ロジックを に組み込むことで解決されました:row-fn。初期の OutOfMemory の問題はj/query、特定の:row-fn.

新しい(作業中の)コードは次のとおりです。

(defn -main []
  (let [; {{{
        db-spec              local-postgres
        source-sql           "select * from public.f_5500 "
        log-report-interval  1000
        fetch-size           1000
        row-count            (atom 0)
        field-delim          "\u0001"   ; unlikely to be in source feed,
                                        ; although i should still check in
                                        ; replace-newline below (for when "\t"
                                        ; is used especially) 
        row-delim            "\n" ; unless fixed-width, target doesn't
                                  ; support non-printable chars for recDelim like 
        db-connection        (doto ( j/get-connection db-spec) (.setAutoCommit false))
        statement            (j/prepare-statement db-connection source-sql :fetch-size fetch-size :concurrency :read-only)
        start                (System/currentTimeMillis)
        rate-calc            (fn [r] (float (/ r (/ ( - (System/currentTimeMillis) start) 100))))
        replace-newline      (fn [s] (if (string? s) (clojure.string/replace  s #"\n" " ") s))
        row-fn               (fn [v] 
                               (swap! row-count inc)
                               (when (zero? (mod @row-count log-report-interval))
                                 (info (format "wrote %d rows" @row-count))
                                 (info (format "\trows/s %.2f"  (rate-calc @row-count)))
                                 (info (format "\tPercent Mem used %s "  (memory-percent-used))))
                               (str (join field-delim (doall (map #(replace-newline %) v))) row-delim ))
        ]; }}}
    (info "Started database table dump session...")
    (with-open [^java.io.Writer wrtr (io/writer "./sql/output.txt")]
      (j/query db-connection [statement] :as-arrays? true :row-fn 
               #(.write wrtr (row-fn %))))
    (info (format "\t\t\tCompleted with %d rows" @row-count))
    (info (format "\t\t\tCompleted in %s seconds" (float (/ (- (System/currentTimeMillis) start) 1000))))
    (info (format "\t\t\tAverage rows/s %.2f"  (rate-calc @row-count)))
    nil)
  )

私が実験した他のこと(限られた成功で)には、音色のロギングと標準出力のオフが含まれていました。REPL を使用すると、エディター (vim の暖炉) に表示する前に結果がキャッシュされるのではないかと考えましたが、それが多くのメモリを使用しているかどうかはわかりませんでした。

また、メモリ空き周りのロギング部分を で追加しました (.freeMemory (java.lang.Runtime/getRuntime))。私は VisualVM に精通しておらず、問題がどこにあるかを正確に特定していませんでした。

私は今のやり方に満足しています。皆さんの助けに感謝します。

于 2013-11-06T05:27:31.470 に答える