簡體   English   中英

使用 bash 或 python 對巨大的 JSON 文件進行排序

[英]Sort huge JSON file using bash or python

要求:我有一個.gz格式的Json文件。 因此,當它被壓縮時,它的大小約為 500 MB。 當我提取它時,json 文件幾乎變成了 ~10 GB。 提取的 JSON 文件逐行包含各個 JSON 對象。我想要的是使用任何 bash 腳本或 python 程序根據字段ps對文件進行排序。

因為文件太大,不建議加載到內存中。 因此,我使用 gzcat 和 cat bash 命令來流式傳輸 JSON 數據,然后將它們通過管道傳輸到 jq 以進行排序。 但是要么系統在此過程中沒有響應,要么我在 output.json 中得到空文件

>cat  sth2.json | parallel --pipe --group --block 1000M --recend '\n}\n' "jq -s -c 'sort_by(.ps) | .[]'"  > "output.json"
>gzcat  sth2.json.gz | parallel --pipe --group --block 1000M --recend '\n}\n' "jq -s -c 'sort_by(.ps) | .[]'"  > "output.json"

硬件:16GB 內存,酷睿 i5 處理器

示例 JSON 數據:-

{
    "ps":"abc"
    ....
}
{   
    "ps":"def"
    ......
}
{
    "ps":"abc"
    ....
}

預期輸出

{
    "ps":"abc"
    ....
}
{   
    "ps":"abc"
    ....
}
{
    "ps":"def"
    ....
}

我不明白我做錯了什么。 誰能建議如何對如此巨大的 JSON 文件進行排序? 我關注的鏈接: https ://github.com/joelpurra/jq-hopkok/tree/master/src/parallelism

另外,有什么方法可以在沒有 Hadoop 的情況下通過任何 Map reduce 來做?

方法 1:將數據流式傳輸到本地 Sqlite 數據庫。

import sqlite3
import fileinput

PATH=".../sqlite-snapshot-201904101324/testDB.db"
insert_query="INSERT INTO feeds (data) VALUES (?)"

def db_connect(db_path=PATH):
    con = sqlite3.connect(db_path)
    return con

con = db_connect() # connect to the database
cur = con.cursor() # instantiate a cursor obj

record_count = 0
for line in fileinput.input():
    cur.execute(insert_query,(line,))

命令行:

>gzcat sth.json.gz | python insert.py

這是基於其中一條評論中的建議的解決方案:

例如,如果您可以在行前加上排序鍵,以便可以將它們作為文本而不是 JSON 進行排序,那么 GNU sort 可以輕松地對 10GB 以上的文件進行排序,而無需將它們加載到內存中。 – 另一個人

您可以使用 jq 按照以下幾行執行此操作:

jq -cr '"\(.ps)\t\(.)"' 

這將生成具有制表符分隔值的行,如下所示:

abc {"ps":"abc","x":0}
abc {"ps":"abc","x":1}

使用 -c 選項可確保將每一對(即排序鍵和對象)寫入一行。

現在您可以輕松地對行進行排序,例如使用sort 然后使用例如cut去除 .ps 字段。

最后,如果你真的想要格式化輸出,你可以再次使用jq (例如jq. ),關鍵是 jq 默認是面向流的。

警告

以上假定 .ps 值是無制表符的。 如果不是這種情況,那么您可以使用不同的字段分隔符,或者:

jq -cr '([.ps] | @tsv) + "\t" + tostring'

您不必擔心不同類型的邊緣情況,而是可以使用 base64 編碼和解碼。

此方法創建兩個制表符分隔的列。 第一列包含用於排序的值。 第二個是 base64 格式的完整 JSON。

排序后我們得到第二列和 base64 解碼。

gzcat sth.json.gz | \
  | jq -cr '[(. | .ps | @text), (. | @base64)] | @tsv' \ # Create a TSV where the first column contains the value that should be sorted on.
  | sort -t$'\t' -k1,1 \ # Sort only on first column.
  | cut -f 2 \ # Get the base64 encoded JSON from seconds column.
  | base64 -d \ # Decode the base64 encoded JSON. (Ignores newlines)
  | jq -c . # Required to place each JSON on separate line.

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM