所有。
我想用Clojure解析大日志文件
并且每行记录的结构是“UserID,Lantitude,Lontitude,Timestamp”
我实施的步骤是:
---->阅读日志文件&获取前n个用户列表
---->查找每个前n个用户的记录并存储在单独的日志文件(UserID.log)中。
工具源代码:
;======================================================
(defn parse-file
""
[file n]
(with-open [rdr (io/reader file)]
(println "001 begin with open ")
(let [lines (line-seq rdr)
res (parse-recur lines)
sorted
(into (sorted-map-by (fn [key1 key2]
(compare [(get res key2) key2]
[(get res key1) key1])))
res)]
(println "Statistic result : " res)
(println "Top-N User List : " sorted)
(find-write-recur lines sorted n)
)))
(defn parse-recur
""
[lines]
(loop [ls lines
res {}]
(if ls
(recur (next ls)
(update-res res (first ls)))
res)))
(defn update-res
""
[res line]
(let [params (string/split line #",")
id (if (> (count params) 1) (params 0) "0")]
(if (res id)
(update-in res [id] inc)
(assoc res id 1))))
(defn find-write-recur
"Get each users' records and store into separate log file"
[lines sorted n]
(loop [x n
sd sorted
id (first (keys sd))]
(if (and (> x 0) sd)
(do (create-write-file id
(find-recur lines id))
(recur (dec x)
(rest sd)
(nth (keys sd) 1))))))
(defn find-recur
""
[lines id]
(loop [ls lines
res []]
(if ls
(recur (next ls)
(update-vec res id (first ls)))
res)))
(defn update-vec
""
[res id line]
(let [params (string/split line #",")
id_ (if (> (count params) 1) (params 0) "0")]
(if (= id id_ )
(conj res line)
res)))
(defn create-write-file
"Create a new file and write information into the file."
([file info-lines]
(with-open [wr (io/writer (str MAIN-PATH file))]
(doseq [line info-lines] (.write wr (str line "\n")))
))
([file info-lines append?]
(with-open [wr (io/writer (str MAIN-PATH file) :append append?)]
(doseq [line info-lines] (.write wr (str line "\n"))))
))
;======================================================
我使用命令(解析文件“./DATA/log.log”3)在REPL中测试了这个clj,并得到结果:
记录-----大小-----时间----结果
1000 ------- 42KB -----< 1s ----- OK
万------ ---- 420KB< 1S -----行
100000 ----- 4.3MB ---- ------ 3S OK
百万--- 43MB ----- ----- 15秒OK
6,000,000 --- 258MB ----> 20M ----“OutOfMemoryError Java堆空间java.lang.String.substring(String.java:1913)”
=============================================== =======
这是一个问题:
1.当我尝试解析大日志文件时如何修复错误,例如> 200MB
2.如何优化功能以更快地运行?
3.有超过1G大小的日志,该功能如何处理它。
我还是Clojure的新手,任何建议或解决方案都会受到赞赏〜
感谢
答案 0 :(得分:7)
直接回答你的问题;从一点点Clojure经验。
内存耗尽的快速而肮脏的修复归结为为JVM提供更多内存。您可以尝试将其添加到project.clj
:
:jvm-opts ["-Xmx1G"] ;; or more
这将使Leiningen推出具有更高内存容量的JVM。
无论你如何工作,这种工作都会占用大量的内存。 @Vidya建议使用图书馆绝对值得考虑。但是,您可以制作的一项优化应该会有所帮助。
每当你处理你的(line-seq ...)
对象(一个懒惰的序列)时,你应该确保将它保持为懒惰的seq。在其上执行next
会立即将整个内容拉入内存。请改用rest
。看一下clojure网站,特别是关于laziness的部分:
(休息 aseq) - 返回一个可能为空的seq,永远不会为
[剪断]
(可能)延迟到剩余项目的路径(如果有的话)
您甚至可能希望遍历日志两次 - 一次只将每行的用户名拉为lazy-seq,再次过滤掉这些用户。这样可以最大限度地减少您在任何时候保留的文件数量。
确保您的函数是惰性的应该减少将文件作为内存序列创建的纯粹开销。这是否足以解析1G文件,我认为我不能说。
答案 1 :(得分:1)
你绝对不需要Cascalog或Hadoop来解析一个不适合你的Java堆的文件。 This SO question提供了一些如何懒惰处理大型文件的工作示例。重点是你需要在遍历lazy seq时保持文件打开。在类似的情况下,这对我有用:
(defn lazy-file-lines [file]
(letfn [(helper [rdr]
(lazy-seq
(if-let [line (.readLine rdr)]
(cons line (helper rdr))
(do (.close rdr) nil))))]
(helper (clojure.java.io/reader file))))
你可以通过这个懒惰的序列map
,reduce
,count
等等:
(count (lazy-file-lines "/tmp/massive-file.txt"))
;=> <a large integer>
解析是一个单独的,更简单的问题。
答案 2 :(得分:0)
我对Clojure也比较陌生,所以我看不到明显的优化。希望其他更有经验的人可以提供一些建议。但我觉得这只是数据大小对于手头的工具来说太大了。
出于这个原因,我建议使用Cascalog,使用Clojure对Hadoop或本地机器进行抽象。我认为查询大日志文件的语法对你来说非常简单。