0

[Backend Tips] JSON vs JSONL: Đừng để "Dấu phẩy" làm nghẽn hệ thống của bạn

1. Đặt vấn đề: Khi file JSON 10GB "phản chủ"

Chắc hẳn ai trong chúng ta cũng quen thuộc với JSON – "ông vua" của việc truyền tải dữ liệu web. Thế nhưng, hãy tưởng tượng bạn có một file log hoặc dataset nặng 10GB được lưu dưới dạng một JSON Array khổng lồ.

Để đọc được một bản ghi cuối cùng, server của bạn phải:

  1. Load toàn bộ 10GB vào RAM.
  2. Parse cấu trúc dấu ngoặc [] và dấu phẩy ,.
  3. Nếu chẳng may file bị lỗi một dấu phẩy ở giữa? Chúc mừng, toàn bộ file của bạn trở nên vô dụng (invalid).

Đó là lúc JSONL (JSON Lines) xuất hiện như một "vị cứu tinh".

2. Định nghĩa nhanh

JSON (Standard) JSON là một cấu trúc dữ liệu phân cấp, bắt đầu bằng {} (Object) hoặc [] (Array). Các phần tử phân cách nhau bằng dấu phẩy.

[
  {"id": 1, "name": "Gemini"},
  {"id": 2, "name": "Viblo User"}
]

JSONL (JSON Lines / ndjson) JSONL là định dạng mà mỗi dòng là một đối tượng JSON hợp lệ. Không có dấu ngoặc vuông bao quanh toàn bộ file, và không có dấu phẩy phân cách giữa các dòng.

{"id": 1, "name": "Gemini"}
{"id": 2, "name": "Viblo User"}

3. Bảng so sánh chi tiết

Đặc điểm JSON (Standard) JSONL (JSON Lines)
Cấu trúc Là một khối thống nhất (Single object/array). Là danh sách các dòng độc lập.
Khả năng đọc Phải đọc toàn bộ file vào bộ nhớ để parse. Có thể đọc từng dòng (Streaming).
Ghi dữ liệu Phải đọc lên, chèn vào array rồi ghi lại (hoặc tốn công xử lý con trỏ). Chỉ cần append (ghi thêm) vào cuối file.
Xử lý lỗi Một lỗi nhỏ làm hỏng cả file. Lỗi dòng nào, chỉ hỏng dòng đó. Các dòng khác vẫn ổn.
Dung lượng Thường lớn hơn một chút do các ký tự format. Tối ưu cho việc lưu trữ dữ liệu lớn (Big Data).

4. Tại sao JSONL lại "vô đối" trong Big Data và Logging?

Cực kỳ tiết kiệm RAM

Với JSONL, bạn có thể xử lý file 100GB trên một chiếc laptop có 8GB RAM bằng cách đọc từng dòng một (Stream processing). Độ phức tạp không gian (Space Complexity) lúc này chỉ là O(1)O(1) cho mỗi đơn vị dữ liệu, thay vì O(n)O(n) như JSON truyền thống.

Khả năng "chịu đòn" (Robustness)

Nếu hệ thống đang ghi log mà bị sập nguồn, file JSON truyền thống sẽ bị thiếu dấu đóng ngoặc ] và trở nên lỗi. Với JSONL, những dòng đã ghi xong trước đó vẫn hoàn toàn hợp lệ.

Dễ dàng cho việc lọc dữ liệu (Grep/Awk)

Vì mỗi bản ghi nằm trên một dòng, bạn có thể dùng các lệnh terminal cơ bản như grep để tìm kiếm dữ liệu cực nhanh mà không cần parse toàn bộ cấu trúc.

Khi nào nên dùng cái nào?

Nên dùng JSON khi:

Làm việc với REST API: Response trả về có cấu trúc rõ ràng, kích thước vừa phải.

File cấu hình (Config): Nơi tính phân cấp (nested) được ưu tiên và dữ liệu không thay đổi liên tục.

Khi frontend cần consume dữ liệu trực tiếp và không muốn xử lý việc tách dòng.

Nên dùng JSONL khi:

Logging: Ghi lại lịch sử hoạt động của hệ thống theo thời gian thực.

Data Pipelines: Truyền dữ liệu giữa các công đoạn trong xử lý Big Data (Spark, Flink).

Dataset cho AI/LLM: Các file huấn luyện mô hình ngôn ngữ lớn thường dùng JSONL để dễ dàng xáo trộn (shuffle) hoặc lấy mẫu (sample).

Kết luận

JSON sinh ra để dành cho giao tiếp (Communication), còn JSONL sinh ra để dành cho lưu trữ và xử lý dòng dữ liệu (Stream Processing). Việc chọn đúng định dạng không chỉ giúp code của bạn "sạch" hơn mà còn cứu vãn cả hệ thống khi dữ liệu phình to theo thời gian.

Kinh nghiệm xương máu: Nếu dự án của bạn có nguy cơ tăng trưởng dữ liệu theo cấp số nhân, hãy cân nhắc JSONL ngay từ đầu!

Bạn đã từng gặp rắc rối khi parse file JSON "khổng lồ" chưa? Hãy chia sẻ giải pháp của bạn ở phần comment nhé!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí