clojure.java.jdbc/query lazy大结果集

clojure.java.jdbc/query lazy大结果集,jdbc,clojure,Jdbc,Clojure,我试图从数据库中读取数百万行并写入文本文件 这是我问题的继续 我现在的问题似乎是,在程序完成之前,日志记录不会发生。我没有懒散处理的另一个指标是,直到程序完成,文本文件才被写入 根据IRC提示,我的问题似乎与代码的区域中的clojure.java.jdbc/query中的结果集fn和默认值doall有关 我曾尝试用for函数替换它,但仍然发现内存消耗很高,因为它会将整个结果集拖到内存中 我怎样才能有一个:结果集fn,它不会像doall那样把所有东西都拉进来?如何在程序运行时逐步写入日志文件,而不

我试图从数据库中读取数百万行并写入文本文件

这是我问题的继续

我现在的问题似乎是,在程序完成之前,日志记录不会发生。我没有懒散处理的另一个指标是,直到程序完成,文本文件才被写入

根据IRC提示,我的问题似乎与代码的
区域中的
clojure.java.jdbc/query
中的
结果集fn
和默认值
doall
有关

我曾尝试用
for
函数替换它,但仍然发现内存消耗很高,因为它会将整个结果集拖到内存中

我怎样才能有一个
:结果集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))
      )
您可以与
:fetch size
选项一起使用。否则,尽管结果是以延迟序列交付的,但查询本身是急切的

prepare语句
需要一个连接对象,因此需要显式创建一个连接对象。以下是您的使用情况的示例:

(let [db-spec    local-postgres
      sql        "select * from big_table limit 500000 "
      fetch-size 10000 ;; or whatever's appropriate
      cnxn       (doto (j/get-connection db-spec)
                   (.setAutoCommit false))
      stmt       (j/prepare-statement cnxn sql :fetch-size fetch-size)
      results    (rest (j/query cnxn [stmt]))]
  ;; ...
  )
另一个选项

由于问题似乎出在
查询
,请尝试使用查询结果
。它被认为是不推荐使用的,但它仍然存在并且有效。下面是一个示例用法:

(let [db-spec    local-postgres
      sql        "select * from big_table limit 500000 "
      fetch-size 100 ;; or whatever's appropriate
      cnxn       (doto (j/get-connection db-spec)
                   (.setAutoCommit false))
      stmt       (j/prepare-statement cnxn sql :fetch-size fetch-size)]
  (j/with-query-results results [stmt] ;; binds the results to `results`
    (doseq [row results]
      ;;
      )))

我通过将
[org.clojure/java.jdbc“0.3.0-beta1”]
放在我的project.clj依赖项列表中,对
clojure.java.jdbc
进行了最近的修复。这一个增强/修正了
:作为数组?true
描述了
clojure.java.jdbc/query的功能

我认为这有所帮助,但是我可能仍然能够覆盖
:结果集fn
vec

通过将所有行逻辑塞入
:row fn
解决了核心问题。最初的OutOfMemory问题与迭代
j/query
结果集有关,而不是定义特定的
:行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 fireplace)之前缓存结果,我不确定这是否占用了大量内存

另外,我使用
(.freemory(java.lang.Runtime/getRuntime))
添加了关于内存空闲的日志部分。我不太熟悉VisualVM,也不太清楚我的问题所在


我对它现在的工作方式很满意,谢谢大家的帮助

我找到了一个更好的解决方案:您需要在事务中声明一个游标并从中获取数据块。例如:

  (db/with-tx
    (db/execute! "declare cur cursor for select * from huge_table")
    (loop []
      (when-let [rows (-> "fetch 10 from cur" db/query not-empty)]
        (doseq [row rows]
          (process-a-row row))
        (recur))))
这里,
db/with-tx
db/execute
db/query
是我自己在
db
命名空间中声明的快捷方式:

(def ^:dynamic
  *db* {:dbtype "postgresql"
        :connection-uri <some db url>)})

(defn query [& args]
  (apply jdbc/query *db* args))

(defn execute! [& args]
  (apply jdbc/execute! *db* args))

(defmacro with-tx
  "Runs a series of queries into transaction."
  [& body]
  `(jdbc/with-db-transaction [tx# *db*]
     (binding [*db* tx#]
       ~@body)))
(def^:动态
*db*{:dbtype“postgresql”
:连接uri)})
(defn查询[&args]
(应用jdbc/query*db*args))
(defn执行![&args]
(应用jdbc/execute!*db*args))
(与tx一起使用)
“在事务中运行一系列查询。”
[&正文]
`(jdbc/with db事务[tx#*db*]
(绑定[*db*tx#]
(~@body))

我添加了连接,将:result类型设置为forward only,添加了游标,将其设置为:read only,并将fetch size设置为1000,然后设置为100。当我尝试获取更大的结果集时,jvm堆的大小仍然不足。我已经更新了上面的问题,加入了新的代码。。。我不知道此时该做些什么…@joefromct,尝试禁用自动提交-
(.setAutoCommit db connection false)
。我在回答中把它添加到示例代码中。另一方面,部分困难在于
setFetchSize
仅仅是对驱动程序()的一个提示,因此它的解释方式可能因驱动程序而异。然而,PostgreSQL表明它是受支持的,所以我认为我们只需要找到正确的咒语。为了澄清,
setFetchSize
是一个方法
prepare语句
根据
:fetchSize
参数在内部调用,而不是代码中需要的额外内容。@joefromct,我刚刚意识到我昨晚添加的示例中有一个错误。在运行查询之前,您需要调用
.setAutoCommit
。我已经纠正了这个例子。很抱歉,我有点累了。还是没有运气。。。。我在发出查询之前添加了.setAutocommit。我还与IRC聊天中的一些人进行了交谈,有人打开了这个窗口:我已经更新了我的代码,以反映我的最新尝试和您的建议。OutOfMemory JavaHeapSpace。谢谢你,我来试试。不幸的是,
db/execute!中必须使用特定的pg-sql语法但是。我当时在研究生院实习,但试图建立一些数据库不可知的东西。谢谢