Clojure - 加速大文件处理

7
我需要读取一个大文件(~1GB),处理它并保存到数据库。我的解决方案如下: data.txt 格式:[id],[标题]\n
1,Foo
2,Bar
...

代码

(ns test.core
  (:require [clojure.java.io :as io]
            [clojure.string :refer [split]]))

(defn parse-line
  [line]
  (let [values (split line #",")]
    (zipmap [:id :title] values)))

(defn run
  []
  (with-open [reader (io/reader "~/data.txt")]
    (insert-batch (map parse-line (line-seq reader)))))

; insert-batch just save vector of records into database

但是这段代码运行效果不佳,因为它首先解析所有行,然后再将它们发送到数据库。

我认为理想的解决方案应该是 读取一行 -> 解析一行 -> 收集1000个解析好的行 -> 批量插入到数据库中 -> 重复此过程直到没有可插入的行。不幸的是,我不知道如何实现这个方案。

1个回答

18

一个建议:

  • 使用 line-seq 获取行的惰性序列,

  • 使用 map 解析每一行,

(目前这与您正在做的相符)

  • 使用partition-all将已解析的行的惰性序列分成批次,然后

  • 使用 insert-batch 与 doseq 将每个批次写入数据库。

以下是一个示例:

(->> (line-seq reader)
     (map parse-line)
     (partition-all 1000)
     (#(doseq [batch %] 
       (insert-batch batch))))

非常好用!非常感谢。我发现我在理解惰性序列方面存在问题。您知道有哪些好的资源可以帮助我更好地理解它吗? - user1518183
如果您想并行处理分区,该怎么办?是否有类似于doseqpmap等效函数或其他推荐的习惯用语来管理一堆代理作为执行器? - matanster
自Clojure 1.7以来,您可以将最后一部分简单替换为(run! Insert-batch)。另外,应该使用insert-batch!因为它会修改状态(在数据库中)。 - siltalau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接