2013-11-20 3 views
3

все.
Я хочу разбирать большие файлы журнала с помощью Clojure.
И структура каждой записи строки - «UserID, Lantitude, Lontitude, Timestamp».
Мои реализованные шаги:
----> Читать файл журнала & Получить топ-N Список пользователей
----> Найти записи и хранения каждого топ-н пользователя в отдельный лог-файл (UserID.log).Clojure: Получить «OutOfMemoryError Java кучу пространства» при разборе большого файла журнала

Орудие исходный код:

;====================================================== 
(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")))) 
    )) 
;====================================================== 

Я испытал это CLJ в РЕПЛ с командой (синтаксический-файл "./DATA/log.log" 3), и получить результаты:

Записи ----- Размер ----- Время ---- Результат
1,000 ------- 42KB ----- < 1s ----- OK
10 000 ----- -420 КБ ---- < 1s ----- OK
100 000 ----- 4.3MB ---- 3s ------ OK
1,000,000 --- 43MB ----- 15s ----- OK
6,000,000 --- 258MB ----> 20M ---- "OutOfMemoryError Java куча пространства java.lang.String.substring (String. java: 1913) "

============================================================================================================================================== ==============
Вот вопрос:
1. как я могу исправить ошибку, когда я пытаюсь разобрать большой файл журнала, как> 200MB
2. как я могу оптимизировать функцию для работы быстрее?
3. Есть журналы размером более 1G, как с этим можно справиться.

Я еще новичок в Clojure, любое предложение или решение будет оценить ~
Благодаря

ответ

0

Я также относительно нового для Clojure, так что нет никаких очевидных оптимизаций я могу видеть. Надеюсь, другие более опытные могут предложить некоторые советы. Но я чувствую, что это просто вопрос о том, что размер данных слишком велик для инструментов.

По этой причине я бы предложил использовать Cascalog, абстракцию над Hadoop или вашу локальную машину, используя Clojure. Я думаю, что синтаксис для запросов к большим файлам журналов был бы довольно простым для вас.

7

В качестве прямого ответа на ваши вопросы; от небольшого опыта Clojure.

  1. Быстрое и грязное исправление для исчерпания памяти сводится к тому, чтобы увеличить объем памяти JVM. Вы можете попробовать добавить это к вашим project.clj:

    :jvm-opts ["-Xmx1G"] ;; or more 
    

    Это сделает Leiningen запуска виртуальной машины Java с более высокой крышкой памяти.

  2. Этот вид работы будет использовать большую память независимо от того, как вы ее работаете. @ Предложение Видьи об использовании библиотеки, безусловно, стоит рассмотреть. Тем не менее, есть одна оптимизация, которую вы может сделать, что должно помочь немного.

    Всякий раз, когда вы имеете дело с объектом (line-seq ...) (ленивая последовательность), вы должны быть уверены, что будете поддерживать его как ленивый сегмент. Выполнение next на нем сразу вытащит все это в память. Вместо этого используйте rest. Посмотрите на сайте Clojure, особенно раздел о laziness:

    (остальные aseq) - возвращает возможно, пустой SEQ, никогда не ноль

    [надрез]

    (возможно) отложенный путь к остальным предметам, если они есть

    Вы можете даже захотеть пройти журнал дважды - один раз, чтобы вытащить только имя пользователя из каждой строки как ленивый-seq, снова отфильтровать t пользователей шлангов. Это позволит свести к минимуму количество файлов, которые вы держите в любой момент времени.

  3. Убедившись, что ваша функция ленива, необходимо уменьшить объем служебных данных, которые имеют файл в виде последовательности в памяти. Достаточно ли этого, чтобы разобрать файл 1G, я не думаю, что могу сказать.

+0

Я думал, что увеличение памяти будет просто лечить симптом, а не лечить болезнь, но ваша точка на * поддержании * лень велика. – Vidya

1

Вам определенно не нужен Cascalog или Hadoop просто для анализа файла, который не вписывается в вашу кучу Java. This SO question содержит несколько рабочих примеров того, как обрабатывать большие файлы лениво. Главное - вам нужно держать файл открытым, пока вы проходите мимо ленивого 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> 

Разбор отдельная, простая задача.

Смежные вопросы