2016-10-22 3 views
6

큰 json 파일 (3GB)을 구문 분석하고이 파일의 각 행에 대한 해시 맵을 반환합니다. 필자의 직감은 변환기를 사용하여 파일을 한 줄씩 처리하고 일부 선택된 필드 (파일의 바이트의> 5 %)가있는 벡터를 구성하는 것이 었습니다. 내가 JVisualVM와 과정을 시각화 할 때, 힙을 통해 성장Clojure Tranducers를 사용하여 큰 파일 구문 분석 : OutOfMemory 오류

(defn load-with! 
    "Load a file using a parser, a structure and a transducer." 
    [parser structure xform path] 
    (with-open [r (clojure.java.io/reader path)] 
    (into structure xform (parser r)))) 

(def xf (map #(get-in % ["experiments" "results"]))) 
(def parser (comp (partial map cheshire.core/parse-string) line-seq)) 

(load-with! parser (vector) xf "file.json") 

file.json

{"experiments": {"results": ...}} 
{"experiments": {"results": ...}} 
{"experiments": {"results": ...}} 

parser.clj :

그러나 다음 코드는에서 OutOfMemory 예외를 발생 프로세스가 충돌하기 전에 25GB를 초과합니다.

이 경우 적절한 변환기가 있습니까? 거기에 더 나은 대안이 있습니까?

함수 끝에 새 구조체를 반환해야한다는 요구 사항 중 하나입니다. 따라서, dosq을 사용하여 파일을 내부 처리 할 수 ​​없습니다.

또한 파서와 변환기를 파일 형식에 맞게 변경해야합니다.

감사합니다.

+0

코드를 완전히 이해하지 못했습니다. 파서의 역할은 무엇입니까? 통과되었지만 사용되지 않은 것 같습니다. 또한'(r)'표현은 아마도 당신이 원하는 것이 아니며 독자를 함수로 부릅니다. –

+2

트랜스 듀서가 도움이되는 이유가 없습니다. 트랜스 듀서는 데이터에 대해 수행하려는 일련의 작업이있을 때 유용합니다. 변환기를 사용하면 버려 질 중간 데이터 구조를 만들지 않아도됩니다. 이 코드는 단지 한 가지만 수행합니다.'get-in'을 매핑합니다. 'into '는 non-lazy이다. 파일을 지연 처리 할 수 ​​있습니까? 'for ','map' 또는'sequence' 변환기 함수를 사용하면 게이츠 시퀀스의 게으른 시퀀스를 만들 수 있습니까? 파일을 올바르게 처리하면 모든 파일 내용을 메모리에 보관하지 않고 각각을 처리 할 수 ​​있습니다. – Mars

+0

파서/변환기의 목표는 파일 형식 (예 : json, csv ...) 및 파일 내의 공급 업체 형식에 따라 작업을 쉽게 적용하는 것입니다. – fmind

답변

1

꽤 가깝습니다. json/parse-string이 무엇인지 모르겠지만, json/read-str에서 here까지 같은 코드가 있다면이 코드는 당신이 거기에서 무엇을하려고하는지 알아야합니다.

(require '[clojure.data.json :as json]) 
(require '[clojure.java.io :as java]) 

(defn load-with! 
    "Load a file using a parser, a structure and a transducer." 
    [parser structure xform path] 
    (with-open [r (java/reader path)] 
    (into structure (xform (parser r))))) 

(def xf (partial map #(get-in % ["experiments" "results"]))) 

(def parser (comp (partial map json/read-str) line-seq)) 


(load-with! parser [] xf "file.json") 

내가이 여기에 당신의 최소한의 예에 모든 사업의 세부 사항을 절단 만든 단지 실수했다 같은데요 :이 같은가는 것처럼

는 것 같습니다. 아래 코드를 사용하여 위의 코드에서 OOM 오류가 발생한 큰 파일을 처리 할 수있었습니다.

(require '[clojure.data.json :as json]) 
(require '[clojure.java.io :as java]) 

(def structure (atom [])) 

(defn do-it! [xform path] 
    (with-open [r (java/reader path)] 
    (doseq [line (line-seq r)] 
     (swap! structure conj (xform line))))) 

(defn xf [line] 
    (-> (json/read-str line) 
     (get-in ["experiments" "results"]))) 

(do-it! xf "file.json") 

(take 10 @structure) 
+0

제안 해 주셔서 감사합니다.이 원자를 사용해야합니다. – fmind

+0

제안 해 주셔서 감사합니다. 전역 변수가 필요합니까? 차이점은 무엇입니까? – fmind

+0

메모리가 충분하면 코드의 첫 번째 비트가 작동합니다. 원자가 doseq와 함께 필요하다고 생각합니다. 나는 이것을 연구하기 위해 시간이 없어서, 나의 대답은 사소한 개선 일 뿐이었다. –

관련 문제