Fail JSON besar tak boleh dibuka? Pilih alat yang betul mengikut saiz
Inti pati: Eksport JSON berpuluh MB kerap membekukan penyunting teks dan alat dalam talian, sedangkan penghuraian sendiri hanya mengambil beberapa ratus milisaat. Kami mengukur masa dan memori pada setiap peringkat untuk sampel 45 MB dengan 500,000 rekod, menunjukkan kos sebenar terletak pada bilangan baris dan nod, serta membina jadual pemilihan alat mengikut saiz fail: paparan dalam talian, baris perintah, pemprosesan strim dan aplikasi desktop.

Fail JSON 45 MB dihurai dalam 249 milisaat. Separuh minit menunggu datang daripada pemaparan lima juta baris teks dan 3.5 juta nod pokok. Silap bezakan kedua-duanya, dan anda akan salahkan pihak yang salah setiap kali.
Satu: ukur dahulu — penghuraian tidak pernah menjadi punca
Klik dua pada eksport .json berpuluh MB, penyunting berpusing tiga puluh saat, dan refleks berkata "penghuraian JSON lambat". Ukuran mengatakan sebaliknya. Pada mesin kami (Node 22), kami menjana sampel 500,000 rekod dengan lima medan setiap satu (id, email, active, score, tags), 45 MB dalam bentuk padat:
| Operasi | Keputusan terukur |
|---|---|
JSON.parse penuh |
249 ms |
JSON.stringify(data, null, 2) — output diformat |
215 ms |
| Teks terformat | 71 MB, 5,000,001 baris |
| Jumlah nod pokok (rekod + medan) | sekitar 3,500,001 |
| RSS proses Node selepas penghuraian | 313 MB (kira-kira 7× saiz fail) |
Penghuraian selesai dalam satu perempat saat. Penghurai sudah selesai kerja sebelum anda lepaskan tetikus. Semua detik selepas itu dibelanjakan pada lapisan "tunjukkan hasil kepada manusia": penyunting teks perlu menyusun lima juta baris, menokok sorotan sintaks dan mengekalkan buffer undo; papar pokok perlu mencipta elemen DOM untuk setiap satu daripada 3.5 juta nod — dan pelayar tersandung sekitar seratus ribu nod, kehilangan tindak balas jauh sebelum sejuta.
Kesimpulan menunjuk ke arah yang sama sekali berbeza: berhenti bergaduh dengan pemaparan — gunakan alat yang tidak memaparkan semuanya sekali gus.
Dua: kenapa membeku — bilangan baris dan nod ialah kos sebenar
Saiz fail hanyalah permukaan. Dua nombor terbitan yang memecahkan alat:
- Bilangan baris. JSON padat 45 MB menjadi lima juta baris apabila diformat. Apa jua bahagian yang anda lihat, penyunting perlu membina struktur baris seluruh fail dahulu, menyelesaikan keadaan sorotan dan memperuntukkan buffer — kos berkadar dengan jumlah baris.
- Bilangan nod. Setengah juta rekod × (satu objek + lima medan) ialah 3.5 juta nod. Sebarang papar yang membina seluruh pokok sebelum memaparkan membayar memori dan masa yang sama — sama ada anda buka satu cawangan atau semuanya.
Dua implikasi melawan intuisi terbit dari sini. Memformat dahulu sebelum melihat merugikan — fail membesar dari 45 ke 71 MB dan bilangan baris membiak empat puluh kali ganda, tekanan tulen tambahan kepada alat. Menyondok fail melalui cat ke terminal turut merugikan — buffer skrol menjadi punca baharu; untuk bait pertama, head -c 2000 sudah cukup.
Tiga: pilih alat mengikut saiz — jadual keputusan
Saiz berbeza menuntut alat yang sama sekali berbeza. Menggabungkan ukuran di atas dengan panduan diterbitkan (OpenFormatter meletakkan zon selesa alat dalam talian pada 2-5 MB; PureJSON menganggarkan memori selepas menghurai dokumen 500 MB sebanyak 2-4 GB), jadual ini terbentuk:
| Saiz fail | Pilihan pertama | Kenapa |
|---|---|---|
| < 2 MB | Sebarang alat dalam talian | Akses segera; papar pokok + pengesahan gaya /parse dalam satu langkah |
| 2 – 20 MB | Penyunting setempat (VS Code) atau alat setempat dalam pelayar | Alat dalam talian mula merosot; pemprosesan setempat tidak menghantar apa-apa |
| 20 – 100 MB | Penerokaan jq melalui CLI + papar pokok maya | Penyunting mula tersentak; jq mengekstrak sebarang serpihan dalam saat |
| 100 MB – 1 GB | jq --stream / ijson, pemprosesan strim |
Memori penghuraian konvensional membesar hingga ~7× saiz fail; tak muat — strimkan |
| > 1 GB atau tambahan berterusan | Tukar format: NDJSON / CSV / Parquet | Satu tatasusunan gergasi perlu dihurai sepenuhnya sebelum rekod pertama berguna; masalah sudah pada format itu sendiri |
Alat /parse di laman ini meliputi tangga pertama: papar pokok dan pengesahan. Fail lebih besar patut ke aplikasi desktop, supaya puluhan MB tidak pernah ditampal ke halaman web.
Empat: jangan buka fail — tanya strukturnya dahulu
Bermuka dengan JSON besar yang asing, langkah pertama bukanlah membuka, tetapi meluangkan beberapa saat memahami bentuknya. jq menjawab tiga soalan dalam sekitar 200 ms:
# Di atas: tatasusunan atau objek? Berapa rekod?
jq 'if type == "array" then {type: "array", count: length} else {type: "object", keys: (keys|length)} end' huge.json
# Bagaimana rupa rekod pertama (satu sahaja, tanpa banjir)?
jq '.[0]' huge.json | head -40
# Semua nama medan (daripada rekod pertama)
jq '.[0] | keys' huge.json
Apabila struktur diketahui, kebanyakan permintaan meruncing kepada "ekstrak satu medan" — fail malah tidak perlu dibuka. Contohnya, untuk mendapatkan emel setiap pengguna:
jq -r '.[].email' huge.json | less
# Tindih penapis
jq -r '.[] | select(.active == true) | .email' huge.json
Peraturannya mudah: untuk "melihat", papar dengan pemaparan maya; untuk "mencari dan mengekstrak", jq. Jangan campurkan dua peranan dalam satu alat.
Lima: tebing memori — mod biasa lawan strim, perbezaan 150×
Pada sampel 45 MB yang sama, /usr/bin/time -v mengukur memori puncak jq dalam dua mod:
| Mod | Perintah | Memori puncak (RSS) | Masa |
|---|---|---|---|
| Biasa (muat penuh) | jq '.[0].email' sample.json |
542 MB | 0.7 s |
| Strim | jq --stream 'select(.[0][1]=="email") | .[1]' sample.json |
3.6 MB | 2.5 s |
Mod biasa membaca seluruh fail dan membina graf objek; puncak 542 MB mengesahkan petua "graf objek ≈ 7-12× saiz teks". --stream mengguna input token demi token, mengekalkan memori pada digit tunggal MB — dengan harga masa keseluruhan lebih lama. Melepasi tanda gigabait, perbezaan ini memisahkan "berjalan" daripada "dibunuh oleh OOM killer":
# Untuk fail lebih besar daripada memori: --stream ialah satu-satunya mod yang tidak pernah memuatkan semuanya
jq --stream -n 'fromstream(1|truncate_stream(inputs))' huge.json
Padanan Python ialah ijson: ijson.items(f, 'item') menghasilkan elemen tatasusunan satu demi satu dengan memori malar yang sama. Harga bersama alat-alat ini ialah melepaskan akses rawak — strim ialah satu laluan ke hadapan sahaja, dan laluan kedua bermakna membaca semula fail.
Enam: sejauh mana pelayar mampu — had pemprosesan setempat
Alat dalam talian boleh digunakan, dalam hadnya. Tab pelayar moden mempunyai sekitar 1-4 GB memori; fail 45 MB yang dihurai menjadi graf ditambah pokok biasanya bertahan. Beberapa ratus MB sudah di bibir tebing. Memilih papar dalam talian, tuntut tiga perkara:
- Pemprosesan berlaku secara setempat (kandungan ditampal tidak pernah dihantar ke pelayan) — melindungi data dan menjimatkan masa muat naik; semua alat di laman ini berjalan sepenuhnya dalam pelayar dan berfungsi luar talian;
- Penghuraian berjalan dalam Web Worker — urutan utama tidak membeku, dan tersangkut hanya mengorbankan satu tab, bukan seluruh pelayar;
- Pokok dimayakan atau dilangkengkan — cawangan terlipat hampir tiada kos, hanya yang dibuka dipaparkan.
Di laman ini: /parse sesuai untuk penerokaan struktur dan pencarian ralat bagi fail sehingga beberapa puluh MB; lebih daripada itu, jq atau aplikasi desktop, pelayar disimpan untuk serpihan kecil.
Tujuh: format yang betul menghapus separuh masalah
Jika anda mempengaruhi bagaimana fail dihasilkan, papar yang lebih baik ialah ubat yang salah — pastikan ia tidak membesar menjadi "satu tatasusunan gergasi":
- NDJSON/JSONL: satu rekod setiap baris. Tambahan tidak menulis semula fail, dan
head,grep,tail,splitberfungsi terus; fail terpotong hanya kehilangan baris terakhir. Buku panduan JSONL kami memuatkan prosedur penuh mengendalikan baris rosak. - Eksport berhalaman: API yang memulangkan 100,000 rekod patut menerima parameter halaman — 100 sekaligus lebih mesra kepada kedua-dua pihak.
- Data berjadual bukan JSON: satu rekod setiap baris, medan tetap tanpa sarang — itu jadual. CSV atau Parquet lebih kecil dan ekosistemnya lebih kaya.
Nombor untuk diingati: potong separuh tatasusunan 500,000 rekod, separuh pertama masih fail NDJSON yang sah (separuh rekod terselamat), manakala tatasusunan gergasi tunggal yang setara hilang sepenuhnya. 500 rekod diselamatkan daripada 501 lebih baik daripada sifar.
Lapan: senarai semak
- Teroka struktur dahulu dengan
jq '.[0] | keys'atau sampel 500 rekod; - Jangan sekali-kali memformat semata-mata untuk membuka fail — menggandakan baris hanya melambatkannya;
- Bawah 2 MB: papar pokok dalam talian; atas 20 MB: baris perintah; atas 1 GB: sentiasa
--streamatau ijson; - Pastikan alat dalam talian memproses secara setempat (kandungan ditampal tidak keluar dari pelayar);
- Jika anda kawal penjana, beralih kepada NDJSON atau penghalaman dan hapuskan tatasusunan gergasi di sumbernya.
Soalan lazim
Kenapa penyunting mengambil puluhan saat membuka JSON sedangkan program menghurai dalam 200 ms?
Penghuraian ialah kod natif menukar teks kepada struktur data: 249 ms untuk sampel 45 MB kami. Penyunting tersadai pada persembahan: lima juta baris untuk disusun, disorot dan disimpan sebagai buffer undo; pokok membina DOM untuk 3.5 juta nod. Papar maya atau pengekstrakan terus melalui jq memintas semuanya.
Adakah jq melimpah pada JSON melebihi 1 GB?
Dalam mod biasa, ya: ia memuatkan seluruh dokumen, dan graf objek memuncak pada 7-12× saiz fail. Dengan --stream, input digunakan token demi token: ujian 45 MB kami memuncak pada 3.6 MB, dan memori kekal rata apa jua saiz fail — dengan harga satu laluan berurutan sahaja.
Sebesar mana fail yang boleh ditangani alat JSON dalam talian?
Kebanyakannya selesa sehingga 2-5 MB dan merosot jelas melebihi 10 MB. Had tegar ialah memori tab (kira-kira 1-4 GB), tetapi pada beberapa puluh MB sudah pun menanti penghuraian dan pembinaan pokok menyakitkan. Melebihi 20 MB, gunakan alat setempat atau baris perintah.
Bagaimana melihat nilai satu medan dalam JSON gergasi?
Ekstrak melalui jq tanpa membuka fail: jq -r '.[].email' huge.json mencetak semua emel; dengan penapis — jq -r '.[] | select(.active == true) | .email'. Untuk rekod awal: jq '.[0:5]' huge.json — beberapa magnitud lebih pantas daripada membuka fail.
Memformat dahulu sebelum membuka membantu?
Tidak. Memformat mengubah 45 MB padat menjadi 71 MB dan satu baris menjadi lima juta, menambah beban tulen kepada penyunting dan papar. Simpan pemformatan untuk apa yang akan dibaca manusia — dan hanya jika fail cukup kecil.