Suốt mười một phần, ta đã đi từ entropy — bức tường lý thuyết — qua từng thuật toán (RLE, Huffman, LZ77, DEFLATE), các công cụ (gzip, bzip2, xz, zstd), và cách dùng (mức nén, từ điển, HTTP, Go). Bài cuối này (phần 12 loạt Nén) gom tất cả thành thứ bạn thực sự cần khi ngồi trước một quyết định: chọn cái gì? Câu trả lời ngắn gọn là điều đã lặp đi lặp lại cả loạt — không có "cái tốt nhất", chỉ có "tốt nhất cho ràng buộc của bạn". Nhưng để câu đó có ích, ta cần một khung rõ ràng: bốn tiêu chí để cân, và một cây quyết định để chọn nhanh. Và mình chốt lại bằng một bảng đo thật cuối cùng cho thấy vì sao mỗi công cụ tồn tại.

Bốn tiêu chí, không tối đa cùng lúc

Mọi quyết định nén xoay quanh bốn tiêu chí, và chúng đánh đổi lẫn nhau — không thể tối đa cả bốn:

  1. Tỉ lệ nén — nhỏ hơn bao nhiêu lần. Quan trọng khi tiết kiệm đĩa/băng thông là ưu tiên.
  2. Tốc độ nén — nén nhanh không. Quan trọng khi bạn nén nhiều hoặc realtime.
  3. Tốc độ giải nén — giải nhanh không. Quan trọng khi dữ liệu được đọc nhiều lần.
  4. Bộ nhớ và tính tương thích — RAM cần bao nhiêu, ai đọc được. Quan trọng khi tài nguyên hạn chế hoặc cần phổ biến.

Quy luật chung xuyên suốt loạt: nén chặt hơn thường chậm hơn (phần 6, 7). Nên chọn nghĩa là quyết định tiêu chí nào quan trọng nhất với bạn, rồi chấp nhận đánh đổi các tiêu chí kia.

Ảnh chụp đoạn mã nền tối minh hoạ chọn thuật toán nén khung quyết định theo bốn tiêu chí tỉ lệ tốc độ nén tốc độ giải nén bộ nhớ tương thích không có cái tốt nhất, bốn tiêu chí không thể tối đa cùng lúc 1 tỉ lệ nén nhỏ hơn bao nhiêu tiết kiệm đĩa băng thông 2 tốc độ nén nhanh không quan trọng khi nén nhiều realtime 3 tốc độ giải nén nhanh không quan trọng khi đọc nhiều lần 4 bộ nhớ cộng tương thích ai đọc được RAM hạn chế cần phổ biến đều là đánh đổi nén chặt hơn thường chậm hơn chọn theo ràng buộc của bạn, cây quyết định thực dụng realtime throughput cao log stream RPC DB nóng dùng lz4 hoặc zstd -1 tới -3 nén và giải cực nhanh ratio đủ dùng cân bằng chung backup hàng ngày lưu trữ vừa dùng zstd -3 tới -9 hoặc gzip nhỏ tối đa nén 1 lần đọc nhiều lần asset tĩnh bản phát hành dùng xz -9 zstd -19 brotli -11 chịu nén chậm cần ai cũng đọc được trao đổi công khai HTTP rộng rãi dùng gzip tương thích rộng nhất nhiều mảnh nhỏ tương tự nén giải riêng cache message cột DB dùng zstd cộng dictionary bài 08, ba quy tắc xuyên suốt cả loạt 1 đo trên dữ liệu của bạn tỉ lệ phụ thuộc entropy bài 01 09 không phải thuật toán chạy thử vài công cụ trên mẫu thật rồi chọn 2 đừng nén thứ đã nén mã hóa bài 09 khoảng 1x chỉ tốn CPU 3 zstd là mặc định tốt nhất hiện nay nếu không cần tương thích gzip

Hình 1: Bốn tiêu chí (tỉ lệ, tốc độ nén, tốc độ giải nén, bộ nhớ/tương thích) đánh đổi lẫn nhau; cây quyết định thực dụng chọn công cụ theo ràng buộc; và ba quy tắc xuyên suốt: đo trên dữ liệu của bạn, đừng nén thứ đã nén, zstd là mặc định tốt nhất hiện nay.

Cây quyết định thực dụng

Từ những gì đã đo cả loạt, đây là cách chọn nhanh theo ràng buộc chính:

  • Realtime / throughput cao (log stream, RPC, database nóng) → lz4 hoặc zstd -1..-3: nén và giải cực nhanh, tỉ lệ đủ dùng.
  • Cân bằng chung (backup hằng ngày, lưu trữ vừa) → zstd -3..-9 hoặc gzip.
  • Nhỏ tối đa, nén một lần đọc nhiều lần (asset tĩnh, bản phát hành) → xz -9 / zstd -19 / brotli -11: chịu nén chậm để đổi kích thước nhỏ nhất.
  • Cần ai cũng đọc được (trao đổi công khai, HTTP rộng rãi) → gzip: tương thích rộng nhất.
  • Nhiều mảnh nhỏ tương tự, nén/giải riêng (cache, message queue, cột DB) → zstd + dictionary (phần 8).

Đo thật chốt loạt: mỗi công cụ vô địch một cột

Để thấy vì sao cả năm công cụ đều tồn tại, mình nén cùng một file 22MB bằng lz4, zstd (hai mức), gzip, xz — đo đủ tỉ lệ, tốc độ nén và tốc độ giải nén:

Ảnh chụp bảng kết quả chạy thật chọn thuật toán nén output thật go-lab lz4 zstd gzip xz cùng file log 22039479 byte, bảng đo cuối tỉ lệ vs tốc độ nén vs tốc độ giải nén công cụ size B ratio nén ms giải ms lz4 -1 6921554 3.2x 32 9 nhanh nhất cả 2 chiều zstd -3 4343920 5.1x 60 15 cân bằng tốt nhất gzip -6 3990364 5.5x 290 64 tương thích rộng zstd -19 3014817 7.3x 7145 16 ratio cao cộng giải nhanh xz -9 2917484 7.6x 8853 110 ratio cao nhất chậm, biên Pareto mỗi công cụ tối ưu một ràng buộc khác lz4 ratio thấp nhất 3.2x nhưng nén 32ms cộng giải 9ms vua throughput zstd -3 5.1x trong 60ms điểm cân bằng đẹp mặc định tốt cho đa số zstd -19 7.3x nén chậm 7 giây nhưng giải 16ms nén 1 lần đọc nhiều lần xz -9 7.6x cao nhất nhưng nén 8.8 giây cộng giải 110ms khi dung lượng là vua gzip không vô địch gì nhưng ai cũng đọc được suy ra không có cái tốt nhất chỉ có tốt nhất cho ràng buộc của bạn

Hình 2: Chạy thật trên 22.039.479 byte — lz4 -1 3.2x nhưng nén 32ms/giải 9ms (nhanh nhất); zstd -3 5.1x/60ms (cân bằng); gzip -6 5.5x/290ms (tương thích); zstd -19 7.3x nén chậm 7s nhưng giải 16ms; xz -9 7.6x (chặt nhất) nhưng nén 8.8s/giải 110ms.

  • lz4 — vua tốc độ, đáy tỉ lệ: nén cả 22MB trong 32ms, giải trong 9ms — nhanh nhất cả hai chiều, nhưng tỉ lệ chỉ 3.2x (thấp nhất). Đây là công cụ khi bạn cần nén "gần như miễn phí" trên đường nóng (database WAL, log stream) và tỉ lệ vừa đủ là được.
  • zstd -3 — điểm cân bằng đẹp: 5.1x trong 60ms. Nhanh gần lz4 nhưng nén tốt hơn hẳn. Đây là lý do zstd là mặc định tốt nhất hiện nay cho đa số nhu cầu.
  • gzip -6 — tương thích là giá trị: 5.5x nhưng nén chậm hơn zstd (290ms) và giải chậm hơn (64ms). Nó không thắng tiêu chí hiệu năng nào, nhưng ai cũng đọc được — giá trị của gzip nằm ở tính phổ biến.
  • zstd -19 — nhỏ mà vẫn giải nhanh: 7.3x, nén chậm (7.1 giây) nhưng giải chỉ 16ms — nhanh ngang zstd -3. Tính chất vàng cho "nén một lần đọc nhiều lần" (phần 6): trả chi phí nén một lần, mọi lượt đọc sau siêu nhanh.
  • xz -9 — chặt nhất, chậm nhất: 7.6x (nhỏ nhất) nhưng nén 8.8 giây và giải 110ms. Chỉ đáng khi từng byte dung lượng quan trọng hơn thời gian (gói phát hành, lưu trữ dài hạn).

Năm công cụ, năm điểm khác nhau trên biên Pareto — không cái nào "tốt nhất", mỗi cái tối ưu một ràng buộc. Đúng là bức tranh cả loạt bài đã vẽ.

Tổng kết loạt "Nén dữ liệu: đo thật" (12 phần)

Loạt bài đã đi trọn từ lý thuyết tới thực chiến:

  • Phần 1-4 — nền tảng: entropy (bức tường lý thuyết); RLE (thắng run dài, thua text); Huffman (mã tần suất, tiến sát entropy); LZ77 (loại lặp bằng tham chiếu ngược).
  • Phần 5 — ghép lại: DEFLATE = LZ77 + Huffman, mổ header gzip/zlib.
  • Phần 6-9 — thực tế và đánh đổi: mức nén (lợi ích giảm dần); so gzip/bzip2/xz/zstd; từ điển zstd cho mẩu nhỏ; nén phụ thuộc dữ liệu (entropy quyết định).
  • Phần 10-12 — ứng dụng: nén trong HTTP (Content-Encoding); nén trong Go (streaming); và chọn thuật toán.

Sợi chỉ xuyên suốt, gói trong ba câu: (1) Tỉ lệ nén là thuộc tính của dữ liệu (entropy), không phải thuật toán — đo trên dữ liệu của bạn. (2) Đừng nén thứ đã nén hoặc mã hóa. (3) zstd là mặc định tốt nhất năm nay nếu không cần tương thích gzip. Nén không phải phép thuật — nó là loại bỏ dư thừa trong giới hạn entropy, và biết chọn đúng công cụ cho đúng ràng buộc là kỹ năng phân biệt hệ thống tiết kiệm với hệ thống lãng phí. Cảm ơn bạn đã theo trọn loạt bài, và chúc bạn luôn nén đúng chỗ, đúng cách.

Ba ý mang về

  1. Không có thuật toán nén tốt nhất — chỉ tốt nhất cho ràng buộc: đo thật trên 22MB, lz4 nhanh nhất (32ms/9ms) nhưng 3.2x, xz chặt nhất (7.6x) nhưng chậm nhất, zstd -19 vừa nhỏ (7.3x) vừa giải nhanh (16ms) — mỗi cái vô địch một tiêu chí trên biên Pareto.
  2. Chọn theo cây quyết định bốn tiêu chí: realtime → lz4/zstd thấp; cân bằng → zstd/gzip; nhỏ tối đa (nén 1 lần đọc nhiều) → xz/zstd-19/brotli; tương thích rộng → gzip; nhiều mẩu nhỏ → zstd+dictionary.
  3. Ba quy tắc xuyên suốt loạt: tỉ lệ nén do entropy của dữ liệu quyết định (đo trên dữ liệu của bạn, không chép con số người khác); đừng nén thứ đã nén/mã hóa (~1x, tốn CPU); zstd là mặc định tốt nhất hiện nay khi không cần tương thích gzip.

Nguồn