Data Compression Explained: gzip, Snappy, LZ4 and zstd
Learn how compression shrinks data, why four popular codecs behave so differently, and how to pick the right one for logs, messages, files, and databases without guessing.
Imagine you are packing a suitcase for a long trip. You could throw clothes in as they are, and the suitcase would bulge. Or you could fold shirts, roll socks, and squeeze the air out of a vacuum bag. The clothes are exactly the same, but they now take far less room. Compression does the same thing for data. It finds ways to write the same information using fewer bytes, so it costs less to store and less time to send. The interesting part is that there is no single best way to pack. A careful packer takes longer but fits more. A fast packer is done in seconds but leaves some gaps. gzip, Snappy, LZ4 and zstd are four different packers with four different personalities, and this guide will help you understand each one.
1What Is Compression and Why Does It Exist?
Compression is the art of representing the same information with fewer bytes.
The plain definition
Compression is a technique that takes some data, such as a text file, a log line, a message, or a database page, and rewrites it in a smaller form. Later, a matching step called decompression turns the small form back into the original. The program that does the shrinking and un-shrinking is called a codec, which is short for coder-decoder. gzip, Snappy, LZ4 and zstd are all codecs.
Compression works because real data is rarely random. Human language repeats words. Log files repeat the same timestamps, field names, and error messages thousands of times. JSON repeats the same keys for every record. Whenever data repeats or follows a pattern, a codec can say the pattern once and then point back to it, instead of writing it out every time.
Think of a text message where you write “LOL” instead of “laughing out loud”. Both mean the same thing, but one is shorter. Your friend knows the shortcut, so they can expand it back. A codec creates thousands of tiny shortcuts like this automatically, and the reader knows how to expand every one of them.
Why anyone bothers
There are three big reasons. First, storage costs money. Cutting a data set to one third of its size can cut the disk bill by the same amount. Second, networks have limits. Sending fewer bytes means messages arrive sooner and a link can carry more traffic. Third, many systems are limited by how fast they can read from disk or the network, not by how fast the CPU can think. If reading is the slow part, reading fewer bytes and then spending a little CPU time to expand them can actually make the whole job faster.
Compression is not free. It spends CPU time and memory to save storage and bandwidth. The whole skill of choosing a codec is deciding which resource you are willing to spend and which one you want to save.
2Core Concepts You Need First
A handful of words show up in every compression conversation, so let us define them once.
Lossless Compression
The decompressed data is bit-for-bit identical to the original. All four codecs in this guide are lossless, which is why they are safe for logs, databases, and messages.
Lossy Compression
Some detail is thrown away forever to save space. JPEG photos and MP3 music work this way. It is not suitable for data where every byte matters.
Compression Ratio
Original size divided by compressed size. A ratio of 3 means the data became one third of its size. Higher is better for saving space.
Throughput
How many megabytes per second the codec can compress or decompress. Higher means faster and cheaper in CPU terms.
Sliding Window
The recent chunk of data a codec remembers so it can point back to repeats. A bigger window finds more repeats but needs more memory.
Entropy Coding
A second stage that gives short codes to common symbols and long codes to rare ones. It squeezes extra size out, at extra CPU cost.
Dictionary
A pre-shared block of typical content that both sides know in advance, so even tiny messages can be compressed well.
Compression Level
A dial that tells the codec how hard to search for patterns. Higher levels are smaller but slower to compress.
Compress speed and decompress speed are different things
Beginners often talk about one speed, but there are two. Compression is usually the heavier job because the codec has to search for patterns. Decompression is usually much lighter because it just follows instructions. This matters a lot in real life. Data is often written once and read many times, so a codec that is slow to compress but very fast to decompress can be a great deal.
Many people believe a higher compression level makes reading slower. In most modern codecs, decompression speed barely changes between levels. Only the writing side pays the extra cost.
3How Compression Works Inside
Almost every codec here is built from the same two ideas: remove repeats, then write what is left as compactly as possible.
Stage one: finding repeats with LZ77
The family of ideas behind all four codecs comes from a 1977 method called LZ77, named after Abraham Lempel and Jacob Ziv. The codec walks through the data and keeps a window of the recent bytes. When it sees a sequence it has already seen, it does not write it again. Instead it writes a tiny note that says: go back this many bytes, and copy this many bytes. That note is called a match or a back-reference. Anything that has no earlier copy is written out as it is, and these bytes are called literals.
Suppose you are copying a poem where a line repeats ten times. Instead of writing that line ten times, you write it once and then add “same as the line 3 lines above” nine times. That short note is exactly what a back-reference is.
Stage two: entropy coding
After the repeats are removed, you are left with a stream of literals and match notes. Some of these appear far more often than others. Entropy coding gives short bit patterns to the frequent ones and long patterns to the rare ones, like Morse code giving a single dot to the letter E because it is so common. Huffman coding is the classic way to do this. A newer family called ANS, which zstd uses through a design named Finite State Entropy, reaches similar or better savings while running much faster.
Match Finding
Search the window for earlier copies of the current data. This is the most expensive step in compression and the one that levels tune.
Entropy Coding
Encode literals and matches with variable-length codes. gzip and zstd do this. Snappy and LZ4 skip it to stay fast.
Framing
Wrap the result with headers, block sizes, and checksums so the reader can verify and decode it safely.
flowchart LR
A["Raw data"] --> B["Find repeats in sliding window"]
B --> C["Literals and back-references"]
C --> D{"Entropy coding used?"}
D -->|"gzip and zstd"| E["Huffman or FSE codes"]
D -->|"Snappy and LZ4"| F["Write directly"]
E --> G["Framed compressed output"]
F --> G
Snappy and LZ4 are fast mainly because they skip the entropy stage and search less carefully for matches. gzip and zstd are smaller mainly because they do both stages well. Most of the differences you will see come from this one design choice.
4The Trade-off Triangle
You can rarely have small size, fast compression, and fast decompression all at once.
Picture a triangle with three corners: smallest output, fastest compression, and fastest decompression. A codec sits somewhere inside it. Moving toward one corner usually pulls you away from another. A very small output needs a long search for patterns, which makes compression slow. A very fast compressor cannot afford to search long, so it leaves patterns unfound and the output is larger.
Think of tidying a messy room. A five-minute tidy shoves things into drawers, and the room looks decent. A one-hour tidy sorts everything by type and frees up a whole shelf. Both rooms are tidy, but the longer effort gives you more free space. Finding something later is quick in both cases.
The extra cost that people forget: memory
There is a fourth corner that is easy to miss. Codecs need memory for their window, hash tables, and buffers. If your service runs thousands of compression streams at the same time, such as thousands of connections each compressing their own traffic, the memory per stream adds up quickly. A codec that looks great in a single-file benchmark can become a problem at scale for exactly this reason.
Decide what is scarce
Is your bottleneck disk space, network bandwidth, CPU, or latency? The scarce resource decides which corner of the triangle you lean toward.
Decide how often data is read
Write once and read rarely favors a small, slower-to-write format. Write constantly and read immediately favors a very fast codec.
Measure on your own data
Benchmarks on public data sets are only hints. Your data may compress far better or worse, so test with a real sample.
5gzip: The Universal Classic
gzip is the old reliable of compression. It is everywhere, and almost every tool on earth can read it.
What it is
gzip was created in the early 1990s as a free replacement for older compression tools. The file format is described in RFC 1952, and the compression method inside it is called DEFLATE, described in RFC 1951. DEFLATE combines an LZ77 search with Huffman coding. Because it was standardized so early and so openly, gzip became built into web servers, browsers, operating systems, programming languages, and countless tools.
How it works
gzip looks back over a window of 32 kilobytes to find repeats. It then encodes the literals and matches using Huffman codes, and it can build fresh Huffman tables for each block of data to fit that block well. It adds a small header and a checksum at the end, which lets the reader detect damaged data. Levels run from 1 to 9. Level 1 is the fastest, level 9 is the smallest, and level 6 is the usual default that balances the two.
gzip is like a trusted old moving company that every neighborhood knows. It is not the fastest and it is not the most modern, but everyone accepts its boxes and every building has a door that fits them.
Advantages
- Supported nearly everywhere, from browsers to shell scripts
- Good compression ratio on text, logs, JSON, and HTML
- Low memory use compared to some newer codecs
- Built-in checksum helps detect corruption
- Very stable and thoroughly battle-tested
Disadvantages / Trade-offs
- Noticeably slower to compress than Snappy, LZ4, or zstd
- Decompression is slower than the modern fast codecs
- The 32 KB window misses repeats that are far apart
- The design is old and leaves speed on the table
Whenever you need maximum compatibility, such as serving web content to unknown browsers, sharing files with partners, or archiving data that must be readable by any system decades from now, gzip is a safe and sensible choice.
6Snappy: Built for Speed at Google
Snappy trades some size for a lot of speed, and it made that trade on purpose.
What it is
Snappy came out of Google, where it was used internally for years under an earlier name before being released as open source in 2011. Its goal is very different from gzip. It does not try to produce the smallest output. It tries to keep the CPU cost so low that compression becomes almost invisible in busy systems like databases and distributed processing engines.
How it works
Snappy uses an LZ77-style search but keeps it very simple. It looks at small groups of bytes, uses a quick hash to guess where an earlier copy might be, and moves on quickly when it does not find one. It works on small blocks of around 64 kilobytes. It has no entropy stage at all, so literals are stored as they are and matches are stored in a compact fixed format. There are no levels to tune. Snappy has one speed setting, which is fast.
Snappy is like a courier who grabs the obvious bulky items, throws them in a bag, and runs. They are not reorganizing your whole house, but the bag is noticeably lighter and the courier finishes before you notice.
Advantages
- Very high compression and decompression speed
- Extremely low CPU overhead in busy services
- Simple format that is easy to implement in any language
- Widely supported in big data tools and messaging systems
- No tuning required
Disadvantages / Trade-offs
- Lower compression ratio than gzip and zstd
- No levels, so you cannot trade speed for smaller size
- Not very effective on data that is already compact
- Has lost ground to LZ4 and zstd in many modern designs
Snappy became a default in many older systems, so people assume it is still the best fast option. Today, LZ4 often matches its speed and zstd at low levels often beats its size while staying close on speed. Always compare before assuming.
7LZ4: Speed Above Everything
LZ4 is designed so that decompression runs at speeds close to how fast memory can deliver bytes.
What it is
LZ4 was created by Yann Collet and released around 2011. Its core promise is extremely fast compression and even faster decompression, with a modest ratio. Like Snappy, it is a pure LZ77-style codec with no entropy stage. Its format is designed so that a decoder is little more than a tight loop of copying bytes, which modern CPUs execute very quickly.
How it works
LZ4 encodes data as a series of sequences. Each sequence holds a run of literals followed by a match reference, with small headers that store lengths in a very compact way. The matching window is 64 kilobytes, and the minimum match length is short. The default mode uses a fast hash search. There is also a high-compression variant called LZ4 HC, which searches more thoroughly to get a smaller output while keeping decompression just as fast. This is a very useful property: you can spend extra time once when writing and still enjoy the same lightning-fast reads.
LZ4 is like a zip-lock bag you can seal and open in a heartbeat. It will not shrink your clothes as tightly as a vacuum bag, but opening it takes no effort at all, so you can do it hundreds of times a second.
Advantages
- Among the fastest decompressors available
- Very fast compression in the default mode
- LZ4 HC offers smaller output with unchanged read speed
- Tiny memory footprint
- Great for hot data paths where latency matters
Disadvantages / Trade-offs
- Compression ratio is clearly below gzip and zstd
- Small window limits how far back repeats are found
- No entropy coding, so it leaves size savings unused
- Less suited to cold storage where size matters most
Use LZ4 when compression must never become the bottleneck: in-memory caches, real-time messaging, storage engines that compress on every write, and network transfers inside a fast data center.
8Zstandard (zstd): The Modern All-Rounder
zstd was designed to beat gzip on ratio and speed at the same time, and to cover a huge range of use cases with a single tool.
What it is
Zstandard, usually written zstd, was created by Yann Collet at Facebook and open sourced in 2016. It is standardized in RFC 8878. The goal was ambitious: match or beat gzip on size while being much faster, and also scale down to speeds close to LZ4 or up to very high ratios, all within one codec family. Today it is used in Linux kernels, package managers, databases, log pipelines, and messaging systems.
How it works
zstd starts with a familiar LZ77-style search, but it supports much larger windows than gzip, so it can find repeats that are far apart. It then applies two entropy coders: Huffman for the literals, and Finite State Entropy for the match information. FSE is based on a modern method called Asymmetric Numeral Systems, which gets close to the theoretical best size while running fast. The result is a codec that is both small and quick.
Wide Range of Levels
Levels run from 1 up to 22, plus negative levels that push speed even higher. You can slide from fast to tiny with one setting.
Trained Dictionaries
You can train a small dictionary from samples of your data. Tiny records, like a 300-byte JSON message, then compress far better than they otherwise could.
Long-Range Mode
Lets the codec look very far back, which helps with huge files that contain distant repeats, such as backups and disk images.
Multi-Threading
Large inputs can be split across CPU cores to compress much faster without changing the format.
Fast Decompression
Decompression speed stays high at every level, so heavy compression on write does not slow down reads.
zstd is like a car with a gearbox. In low gear you get tremendous power for a tough climb, meaning very small output. In high gear you cruise fast on flat road, meaning very quick compression. One vehicle handles both, and you simply choose the gear.
Advantages
- Better ratio than gzip at similar or higher speeds
- Can also run close to LZ4 speeds at low levels
- Very fast decompression regardless of level
- Dictionary support for small messages
- Flexible: one codec for both hot and cold data
Disadvantages / Trade-offs
- Not as universally built in as gzip, especially on old systems
- High levels use much more memory and time
- Many options can confuse newcomers
- Dictionaries add operational work to manage and version
9Side-by-Side Comparison
Seeing the four codecs together makes their personalities obvious.
| Feature | gzip | Snappy | LZ4 | zstd |
|---|---|---|---|---|
| Main goal | Compatibility and decent size | High speed, low CPU | Extreme speed | Best balance and flexibility |
| Method | LZ77 plus Huffman | LZ77 only | LZ77 only | LZ77 plus Huffman and FSE |
| Typical ratio on text | Good | Moderate | Moderate | Good to excellent |
| Compression speed | Slow to moderate | Very fast | Very fast | Fast to slow, by level |
| Decompression speed | Moderate | Very fast | Fastest | Very fast |
| Tunable levels | 1 to 9 | None | Fast mode and HC | 1 to 22 and negative levels |
| Window size | 32 KB | Small blocks | 64 KB | Large and configurable |
| Dictionary support | Limited | No | Yes | Yes, with training tools |
| Best fit | Web, archives, sharing | Legacy big data pipelines | Hot paths, caches | General purpose modern choice |
The words here are relative on purpose. Exact numbers depend on your CPU, your data, and the codec version. Treat public benchmark numbers as rough guides, and always measure on a sample of your own data before deciding.
A typical picture in numbers
On a well-known mixed benchmark data set, published results for the zstd project show a familiar pattern. gzip at its default level lands around a 2.7 ratio at roughly 100 megabytes per second compression and around 400 megabytes per second decompression. zstd at its fastest standard level lands around a 2.9 ratio at several hundred megabytes per second compression and well over a thousand megabytes per second decompression. Snappy and LZ4 land around a 2.1 ratio, with LZ4 decompressing fastest of all. These are approximate and vary by machine, but the shape is consistent: zstd tends to beat gzip on both size and speed, and LZ4 wins on raw decompression speed.
quadrantChart
title Speed versus compression ratio
x-axis "Smaller output" --> "Faster speed"
y-axis "Lower efficiency" --> "Higher efficiency"
quadrant-1 "Fast and small"
quadrant-2 "Small but slow"
quadrant-3 "Slow and larger"
quadrant-4 "Fast but larger"
gzip: [0.25, 0.55]
Snappy: [0.75, 0.35]
LZ4: [0.9, 0.3]
zstd: [0.6, 0.8]
10Where Compression Lives in Real Systems
You already use compression every day, even when you never turn it on yourself.
Messaging and Streaming Platforms
Systems like Apache Kafka let you compress batches of messages with gzip, Snappy, LZ4, or zstd. Compressing a whole batch works well because messages in a batch tend to look alike. This saves network bandwidth and broker disk space, and the choice of codec directly affects producer CPU and end-to-end latency.
Columnar Files and Data Lakes
File formats such as Parquet and ORC compress each column separately. Because a column holds similar values, such as thousands of country names in a row, it compresses very well. Snappy has long been the popular default here, while zstd is increasingly chosen for a smaller footprint.
Web Traffic
Web servers compress HTML, CSS, JavaScript, and JSON before sending them to browsers. gzip is universally understood, and newer options such as Brotli and zstd are gaining ground where clients support them. Smaller pages load faster, especially on slow mobile connections.
Databases and Storage Engines
Many databases compress data pages on disk. Fast codecs like LZ4 and Snappy are common because reads happen constantly and must stay quick. Some engines let you choose zstd for tables that are large and rarely read.
Logs and Backups
Log files are highly repetitive and often compress to a fraction of their size. Older logs are commonly stored with a strong setting, such as gzip or a high zstd level, because they are written once and read rarely.
Operating Systems and Packages
Package managers and file systems use compression to make downloads smaller and installs faster. Several Linux distributions adopted zstd for their package formats because it decompresses so quickly.
Hot, frequently accessed data usually gets a fast codec. Cold, rarely accessed data usually gets a strong one. Many teams use two different settings for the same data at different stages of its life.
11Data Flow and Lifecycle of Compressed Data
Following one message from the producer to the reader shows exactly where the cost is paid.
In a typical pipeline, data is compressed once by the writer, stored or transmitted in its small form, and decompressed each time a reader needs it. That means the writer pays the compression cost once, while every reader pays the decompression cost. If you have one writer and fifty readers, decompression speed matters fifty times more often than compression speed.
sequenceDiagram
participant P as Producer
participant C as Codec
participant S as Storage or Network
participant R as Reader
P->>C: Send raw batch
C->>C: Find repeats and encode
C->>S: Write small compressed batch
S->>R: Deliver compressed batch
R->>C: Ask to decompress
C->>R: Return original batch
Batch size changes everything
Compression works best on larger chunks because there are more chances to find repeats. Compressing each tiny message alone finds very little to reuse, and the header overhead can even make a tiny message larger. That is why messaging systems compress whole batches, and why zstd dictionaries exist for cases where you must compress small independent records.
Collect data into a block
Group records together so the codec has enough material to find patterns.
Compress the block
The codec searches for repeats, encodes them, and adds a header and checksum.
Store or send the small block
Disk space and network bandwidth are saved, and the codec name is recorded so readers know how to decode.
Decompress on read
The reader expands the block back to the exact original bytes and verifies the checksum.
Compressing data that is already compressed, such as JPEG images, MP4 videos, or encrypted files, gains almost nothing and wastes CPU. It can even make files slightly bigger.
12How to Choose the Right Codec
A simple set of questions gets you to a good answer for most projects.
flowchart TD
A["Start: what matters most?"] --> B{"Must every client and tool read it?"}
B -->|"Yes"| G["Use gzip"]
B -->|"No"| C{"Is latency or CPU the top concern?"}
C -->|"Yes"| D{"Need slightly smaller output?"}
D -->|"No"| L["Use LZ4"]
D -->|"Yes"| Z1["Use zstd at a low level"]
C -->|"No"| E{"Is storage or bandwidth cost the top concern?"}
E -->|"Yes"| Z2["Use zstd at a medium or high level"]
E -->|"No"| F["Use zstd at its default level"]
Rules of thumb
gzip when
Compatibility beats everything else, or the receiving system only understands gzip.
Snappy when
Your platform already defaults to it and the extra size savings of alternatives do not justify a migration.
LZ4 when
Decompression speed and low latency matter most, such as caches, hot storage, and real-time paths.
zstd when
You want a modern default that can be tuned from very fast to very small, and both ends support it.
If you are starting a new system and both sides support it, zstd is a strong default. It rarely embarrasses you, and if your needs change, you can turn one dial instead of switching codecs and rewriting your pipeline.
13Security Considerations
Compression is a tool, and like any tool it can be misused against you.
Side-channel leaks
When secrets and attacker-controlled text are compressed together and then encrypted, the size of the result can leak information. If an attacker can inject guesses into a page that also contains a secret, a correct guess will compress a little smaller than a wrong one. By watching the sizes, they can slowly reveal the secret. Attacks with names like CRIME and BREACH used this idea against encrypted web traffic. The safest habit is to never compress secrets together with untrusted input before encrypting, or to add protections such as randomizing sizes.
Imagine a sealed suitcase you cannot open, but you can weigh. If you slip in a guessed item and the suitcase gets lighter than expected because it duplicated something already inside, you learn the guess was right. The weight gave the secret away.
Decompression bombs
A tiny, carefully crafted file can expand into gigabytes or more, exhausting memory or disk on the system that opens it. This is called a decompression bomb or zip bomb. Any service that accepts compressed data from outsiders should enforce limits on the expanded size, the expansion ratio, and the time spent decoding.
Integrity and trust
Checksums inside compressed formats help detect accidental damage, but they do not prove the data is authentic. If tampering is a concern, use proper signatures or authenticated encryption in addition to compression.
Problem
A service decompresses whatever clients upload, with no limits on how large the result may grow.
Why It’s Harmful
A single small malicious upload can consume all memory and crash the service, affecting every other user.
Correct Approach
Set a hard cap on decompressed size, watch the expansion ratio, and stop decoding as soon as a limit is crossed.
14Monitoring, Logging and Metrics
You cannot tune what you do not measure, and compression needs measurements from both the size side and the speed side.
Compression Ratio
Track original versus compressed size over time. A sudden drop often means the data changed shape or arrives already compressed.
CPU Time
Measure CPU spent compressing and decompressing. Compare it to the bandwidth or storage saved to see whether the trade is paying off.
Latency Added
Measure how much delay compression adds to each request or message batch, especially at high percentiles.
Memory Use
Watch memory per stream and total memory across all streams, particularly for high levels and large windows.
Error and Corruption Counts
Count checksum failures and decode errors. A rising number can point to bad hardware, broken networks, or version mismatches.
What to log
Record which codec and level produced each block, in the block header or metadata. Future readers will need this to decode correctly, and future you will need it to understand why one data set is bigger than another. When you change a codec, keep the ability to read the old one until all old data has aged out.
Before switching codecs in production, run both on a real sample of your traffic and compare ratio, CPU, and latency side by side. A short test often prevents a costly surprise.
15Design Patterns and Anti-patterns
A few habits keep showing up in well-run systems, and a few mistakes keep showing up in troubled ones.
Helpful patterns
Tiered Compression
Use a fast codec for fresh, hot data, and recompress it with a stronger setting once it becomes old and rarely read. You get quick access when it matters and low storage costs when it does not.
Compress in Batches
Group many small records into one block before compressing. Similar records sit next to each other, so the codec finds many more repeats.
Dictionary for Small Records
When you must compress small independent items, train a dictionary on samples and share it between writer and reader. Version the dictionary so old data stays readable.
Self-Describing Data
Store the codec identifier with the data. Readers can then decode any block without guessing, and you can migrate codecs gradually.
Anti-patterns to avoid
Problem
Using the highest compression level everywhere because smaller sounds better.
Why It’s Harmful
High levels can be many times slower and use far more memory, while saving only a small extra amount of space. Write throughput drops and CPU bills rise.
Correct Approach
Pick the level based on measurements. Reserve high levels for data that is written once, read rarely, and stored for a long time.
Problem
Compressing already compressed or encrypted data, such as images, videos, or ciphertext.
Why It’s Harmful
There are no patterns left to find, so you burn CPU for no savings and may even increase the size slightly.
Correct Approach
Skip compression for these types, or compress before encrypting, keeping the side-channel risks from Chapter 13 in mind.
Problem
Choosing a codec from a single online benchmark without testing your own data.
Why It’s Harmful
Ratios and speeds depend heavily on the shape of the data. A codec that wins on public data may lose on yours.
Correct Approach
Test on a realistic sample from your own system and compare ratio, speed, and memory together.
16Best Practices and Common Mistakes
These habits will save you from most of the pain teams run into.
Start from the bottleneck
Find out whether disk, network, or CPU is the real limit before touching any settings. Compression helps most when I/O is the constraint.
Test with real data
Use samples from production, not made-up files. Include both typical and awkward records.
Match the codec to the data lifecycle
Hot data favors speed. Cold data favors size. Use different settings at different stages.
Set safety limits
Cap decompressed size and time for any data you did not produce yourself.
Plan for change
Store the codec identifier with the data and keep the ability to read older formats, so you can migrate without downtime.
Do This
- Compress in reasonably large batches
- Record the codec and level with each block
- Monitor ratio, CPU, latency, and memory together
- Use dictionaries for small, similar records
- Confirm both sides of a connection support the codec
Avoid This
- Assuming the default codec is always the best
- Ignoring memory use across many parallel streams
- Rolling out a new codec before all readers can decode it
- Compressing secrets together with attacker-controlled text
The most painful compression mistake is switching a writer to a new codec while some readers cannot yet decode it. Always upgrade readers first, then writers.
17A Closer Look at Huffman Coding and FSE
The entropy stage is where gzip and zstd earn their extra savings, so it is worth understanding how it thinks.
The idea behind Huffman coding
Normally every character in a text file takes the same amount of space, eight bits. But in English text, the letter E shows up all the time, while the letter Z is rare. Huffman coding notices this and gives the common symbols short codes, maybe only two or three bits, and gives rare symbols longer codes. Since the common symbols appear so often, the total size drops. The codec builds a small table that lists which code belongs to which symbol, and it stores that table right next to the data so the reader can decode it.
Think of a school where the most popular activities get the shortest nicknames. Everybody says “PE” instead of “physical education” because it comes up daily, but the rarely mentioned “advanced ceramics elective” keeps its long name. Overall, conversations get shorter without losing any meaning.
Why Huffman has a limit
Huffman codes must use a whole number of bits for every symbol. A symbol that ideally deserves only 1.2 bits still has to take at least 1 bit, or more often 2. That small rounding loss adds up across millions of symbols. It is a bit like a shop that can only sell whole loaves of bread even when you only want a little more than one.
What FSE does differently
Finite State Entropy, the method zstd uses for match information, is built on ideas from Asymmetric Numeral Systems. Instead of giving each symbol its own separate code, it keeps a small running state that carries fractional information forward from one symbol to the next. This lets it get very close to the ideal size, without the rounding loss of Huffman, and it does so using a table lookup that runs quickly on modern CPUs. In practice this means better compression at high speed.
Huffman Coding
Whole-bit codes per symbol. Simple, well understood, and used by gzip and by zstd for literals.
Finite State Entropy
Carries a small state between symbols, allowing fractional bits. Used by zstd for match lengths and offsets.
No Entropy Stage
Used by Snappy and LZ4. Bytes are stored as they are, which keeps the decoder extremely simple and quick.
You do not need to implement any of this. But knowing that the entropy stage is the difference between the two codec families explains why gzip and zstd cost more CPU than Snappy and LZ4, and why they save more space.
18Compression Levels and How to Tune Them
A level is a dial that tells the codec how much effort to spend hunting for patterns.
What a level really changes
At a low level, the codec checks only a few places in its window for earlier copies of the current data, and it takes the first decent match it finds. At a high level, it checks many more places, compares several candidates, and sometimes even looks ahead to see whether skipping a match now gives a better result later. More checking means a smaller output, but each extra bit of savings costs more and more time. The curve is steep at the beginning and flat at the end.
Imagine searching for a matching sock. Checking the top of the pile takes five seconds and usually works. Dumping the whole drawer and matching every single pair takes ten minutes and leaves you with only one or two extra pairs. The first few seconds were far more valuable than the last few minutes.
Diminishing returns
With gzip, the jump from level 1 to level 6 gives a real improvement in size, while the jump from level 6 to level 9 gives a small extra gain at a much higher time cost. zstd behaves the same way, only across a wider range. The middle levels are usually the sweet spot. The very top levels of zstd are meant for special jobs, like packaging a software release that will be downloaded millions of times, where spending minutes of compression once is worth it.
Start with the default
Defaults were picked by experts to be a balanced choice for typical data. Begin here and measure before changing anything.
Try one step lower and one step higher
Compare the ratio and time on your real sample. See how much you gain or lose with each step.
Pick the knee of the curve
Look for the level where extra effort stops paying off. Stay just before that point.
Re-check when data or hardware changes
A new data shape or a new CPU can move the best level. Repeat the test occasionally.
Tuning levels on a laptop and then deploying to servers with different CPUs, different core counts, and heavy background load. Real timing in production can differ a lot from a quiet test machine.
19Blocks, Streams, Frames and Formats
Knowing how a codec packages its output helps you avoid confusing errors when systems disagree.
Block compression versus streaming
Some uses compress a fixed block of data in one go, such as a database page or a batch of messages. Others compress a continuous stream, such as a network connection that keeps sending data for hours. Block compression is simple and lets you decompress any block on its own. Streaming carries the codec memory from one piece to the next, which usually improves compression because later data can refer back to earlier data. The price is that you often must read from the start of the stream to decode a later part.
A block is like a sealed envelope you can open at any time and read on its own. A stream is like a long letter whose later pages say “as I mentioned on page two”, so you need the earlier pages to understand the later ones.
Framing matters
The raw compressed bytes are not enough by themselves. A reader also needs to know where each block starts, how long it is, what codec made it, and whether it arrived intact. This packaging is called framing. Different libraries and tools use different framing for the same codec. Snappy is a well-known case: a raw Snappy block, and the framed format meant for streams, are not the same bytes. LZ4 also has a raw block form and a frame form. If one system writes the raw form and another expects the frame form, decoding fails, even though both say they use the same codec.
| Codec | Raw Block Form | Framed or Container Form |
|---|---|---|
| gzip | Raw DEFLATE data | gzip container with header and checksum |
| Snappy | Raw block | Framing format for streams with checksums |
| LZ4 | Raw block | LZ4 frame with header and optional checksum |
| zstd | Frames are the standard form | Frame with header, blocks, and optional checksum |
When a reader complains that data is corrupt right after a codec change, first check whether the writer and reader agree on the framing form, not just the codec name. This mismatch is one of the most common causes of mysterious decode errors.
flowchart LR
A["Writer picks codec and form"] --> B["Header describes format"]
B --> C["Compressed blocks"]
C --> D["Checksum"]
D --> E["Reader parses header"]
E --> F{"Format understood?"}
F -->|"Yes"| G["Decode and verify"]
F -->|"No"| H["Decode error"]
20Dictionaries, Long-Range Matching and Threads
Zstd adds three special tools that solve problems the older codecs cannot.
Dictionaries for tiny data
A codec learns about your data as it reads. When a record is only a few hundred bytes, the codec finishes before it has learned anything useful. A dictionary solves this by giving the codec a head start. You train it on a few thousand sample records, and it captures the common field names, common values, and common phrases. Both the writer and the reader load the same dictionary. Now even a tiny record can point into the dictionary for its repeated parts. For small JSON events, the size reduction from a dictionary can be large compared with compressing without one.
Two friends who share a secret code book can send each other a one-word text that stands for an entire sentence. A stranger would need the full sentence. The shared book is the dictionary.
A dictionary is a piece of shared state. If the reader does not have the exact dictionary that the writer used, the data cannot be decoded. Give every dictionary an identifier, store it with the data, and keep old versions for as long as old data exists.
Long-range matching for huge files
Ordinary windows look back a limited distance. For a large backup or a virtual machine disk image, the same content may repeat hundreds of megabytes apart. Long-range mode lets zstd remember far more history so it can find those distant repeats. It uses more memory, and the reader must also be able to allocate that much, so both sides need to agree on the window size.
Using many cores
Compression of a large input can be split into chunks that run on different CPU cores at the same time. zstd supports this directly, so on a multi-core machine a big compression job can finish several times faster. The chunks are independent enough that the output is still a valid stream any zstd reader can decode. There is a small cost in ratio at chunk boundaries, usually minor.
Dictionary
Best for many small similar records like events, JSON documents, and small database rows.
Long-Range Mode
Best for very large files with repeats that are far apart, like backups and disk images.
Multi-Threading
Best for large one-shot jobs on machines with spare cores, like nightly archive builds.
21Hardware, Memory and Cost Thinking
The right codec depends on the machine it runs on and the money it saves or spends.
CPU and cache behavior
Speed is not only about clever algorithms. Modern CPUs are very fast at simple loops that copy bytes in a predictable pattern and slow when they must jump around memory. That is why LZ4 decompression, which is little more than a tight copy loop, reaches such high speeds. Larger windows and bigger tables, like those used at high zstd levels, do not fit in the fast CPU caches, so speed falls off. Keeping the working memory small can make a codec faster in practice.
Memory per stream
Imagine a server that handles ten thousand connections, and each one keeps its own compression state. If each state needs one megabyte, that is about ten gigabytes of memory just for compression. Lowering the window size or the level can shrink the state dramatically. This is a frequent surprise when a team turns on a stronger setting and then sees memory usage jump.
A simple way to think about cost
Compression saves money in one place and spends it in another. It saves on storage, network transfer, and sometimes reading time. It spends on CPU, and sometimes memory. To decide well, put both sides into the same unit, such as dollars per month, using your real cloud prices. Often the storage savings are large and the CPU cost is small, so a stronger codec wins. In other cases, especially on already busy CPUs, the extra compute is what tips the balance toward a lighter codec.
Example Thought Process: Cold Log Archive
A team stores several terabytes of logs that are almost never read. The extra CPU spent once to compress at a high level is tiny compared to the ongoing storage bill saved every month, so a strong setting is the sensible choice.
Example Thought Process: Busy Message Broker
A broker handles millions of messages a second and is already close to its CPU limit. A very light codec keeps latency steady, even though the network carries a few more bytes.
22How to Run a Fair Compression Test
A careful test takes an hour and can save months of regret.
Collect a realistic sample
Pull data from production, covering different times of day, different record types, and both small and large items. A sample that is too clean gives too rosy a picture.
Keep the conditions equal
Use the same machine, the same block sizes, the same number of threads, and the same library versions for every codec you compare.
Measure all the important numbers
Record compressed size, compression time, decompression time, and peak memory. Looking at only one number leads to bad decisions.
Repeat the runs
Run each test several times and look at the spread. The first run is often slower because of cold caches.
Test under real load
Finally try the winner in a staging system with realistic traffic, to see latency and memory behavior when the machine is busy.
Reading data from a file that is already in memory makes decompression look faster than it will be when data must come from a slow disk or a remote network. Decide whether you are testing the codec alone or the whole pipeline, and be honest about which one you measured.
flowchart TD
A["Collect real sample"] --> B["Run each codec at chosen levels"]
B --> C["Record size, speeds, memory"]
C --> D["Compare against your priorities"]
D --> E{"Clear winner?"}
E -->|"Yes"| F["Pilot in staging under load"]
E -->|"No"| G["Refine sample and levels"]
G --> B
F --> H["Roll out readers first, then writers"]
23A Short History of These Codecs
Each codec was born to solve a problem its creators were facing, and knowing that story explains its personality.
1977: The LZ77 idea
Lempel and Ziv describe the sliding-window method of pointing back to earlier data. Almost every codec in this guide descends from this single idea.
Early 1990s: gzip and DEFLATE
Developers wanted a free, open tool to replace older compression programs with unclear licensing. gzip combined LZ77 with Huffman coding and became the default across the internet.
Around 2011: Snappy and LZ4
Data centers were growing, and CPU time had become precious. Both codecs gave up some size to reach speeds that made compression nearly free for busy systems.
2016: zstd goes open source
Built at Facebook, zstd used newer entropy coding ideas to cover the whole range from fast to small, and later became a formal internet standard.
What the history teaches
Notice the pattern. Each new codec appeared when the surrounding hardware and workloads changed. gzip came when disks and modems were slow and CPUs were modest. Snappy and LZ4 came when servers had many fast cores and the goal was to keep those cores busy with real work. zstd came when engineers realized they could use modern entropy coding tricks to get close to the best of both worlds. Expect the story to continue, because hardware keeps changing and new needs keep appearing.
Older does not mean worse, and newer does not mean better for every job. gzip still solves the compatibility problem it was built for better than anything else.
24Compression in Messaging Systems and Data Files
Two places where most engineers meet these codecs first are message brokers and analytics files, and each has its own habits.
Message brokers
In a broker such as Kafka, a producer collects messages into a batch and compresses the whole batch before sending it. The broker usually stores the batch in its compressed form and hands it to consumers as it is, which means the compression work is done once by the producer and undone once per consumer. This design is efficient, but it has consequences. The larger the batch, the better the compression, but the longer messages may wait for the batch to fill. Settings that control batch size and waiting time therefore affect both compression quality and delivery delay.
Choosing the codec here is a balance. A very light codec keeps producers fast and latency low. A stronger codec reduces network traffic and broker disk usage, which matters when there are many consumers or long retention. Since every consumer must decompress, decompression speed is especially valuable. Also remember that brokers and clients must both support the codec you pick, and older client versions may not know newer codecs.
flowchart LR
P["Producer collects batch"] --> C["Compress batch once"]
C --> B["Broker stores compressed batch"]
B --> K1["Consumer one decompresses"]
B --> K2["Consumer two decompresses"]
B --> K3["Consumer three decompresses"]
Columnar analytics files
Columnar file formats store all the values of one column together. A column of country names, for example, contains the same few dozen values repeated over and over, which is ideal for compression. These formats also apply lightweight tricks before the general codec runs, such as replacing repeated strings with small numbers or storing only the difference between neighboring values. Then a general codec like Snappy or zstd squeezes the result again. Because of this two-layer approach, the ratio you see is often much higher than you would get by compressing a plain text file with the same codec.
Analytics queries usually read the same file many times, so decompression speed is a big deal. This is why fast codecs have been popular defaults. As zstd matured, many teams moved to it because it produces noticeably smaller files while still decompressing quickly, which reduces both storage cost and the time to read from remote object storage.
Changing the codec for new files but forgetting that old files still use the previous one. Mixed data sets are fine as long as every reader can decode both. Always confirm reader support across all tools that touch the data, including reporting tools and export jobs.
25Compression on the Web
The web adds a special rule: the sender and receiver must agree on a codec while they are talking.
How the agreement works
When a browser asks for a page, it includes a header that lists the compression methods it understands. The server looks at this list, picks one it also supports, compresses the response, and adds a header that names the method it used. The browser then decompresses the response before showing the page. Because the browser announces its abilities on every request, servers can safely offer newer methods to modern clients while falling back to gzip for older ones.
Browser announces what it can decode
The request lists the codecs the browser supports, in its preferred order.
Server picks one
It chooses a codec both sides support, balancing size and the CPU it can afford per request.
Server labels the response
A response header tells the browser which codec was used, so it knows how to decode.
Browser decodes and renders
The user sees the page sooner because fewer bytes had to travel.
Static versus dynamic content
Files that do not change, such as scripts and style sheets, can be compressed once ahead of time at a very high level and then served as they are. The expensive work is paid a single time and every visitor benefits. Content that is generated on the fly, such as personalized pages or API answers, must be compressed for every request, so a faster, lighter setting is usually wiser. Mixing these two strategies is common and works well.
Precompress Static Files
Use the strongest setting once during the build. Serve the small file directly to every visitor.
Light Compression for Dynamic Responses
Use a fast level so the server does not add noticeable delay to each response.
Skip Already Compressed Files
Images, video, and fonts are usually compressed already. Leave them alone and save the CPU.
Text-based web content like HTML, style sheets, scripts, and JSON compresses very well, often to a fraction of its original size. Turning compression on for these types is one of the cheapest performance wins in web work.
26Troubleshooting Common Compression Problems
When something goes wrong, the symptom usually points quickly to the cause.
| Symptom | Likely Cause | What to Check |
|---|---|---|
| Data barely gets smaller | Already compressed, encrypted, or random data | Check the data type, and whether an earlier stage already compressed it |
| Decoder reports corrupt or unknown format | Codec or framing mismatch between writer and reader | Confirm the codec name, the framing form, and the library versions on both sides |
| CPU usage jumped after a change | A higher level or a heavier codec was enabled | Review the level, batch size, and how many streams run at once |
| Memory usage jumped | Large windows or many parallel streams | Lower the window or level, or reuse compression contexts |
| Latency rose for small messages | Per-message compression with too little data to gain from | Batch messages together, or use a dictionary |
| Files became larger than the originals | Tiny inputs where header overhead outweighs savings | Skip compression for very small payloads |
| Old data will not open after a migration | Support for the previous codec was removed too early | Restore old-codec readers until all old data is rewritten |
A calm way to investigate
Start by asking three questions. First, did something change recently, such as a codec, a level, a library version, or the shape of the data? Second, is the problem in size, in speed, or in correctness? Third, can you reproduce it with a small sample outside production? Answering these usually narrows the search quickly. Most compression trouble turns out to be a mismatch between two parts of a system rather than a fault in the codec itself.
Problem
Deleting support for an old codec as soon as new writes use a new one.
Why It’s Harmful
Old files, old messages, and old backups still need it. Restoring from a backup during an emergency is exactly when you find out they cannot be read.
Correct Approach
Keep decoders for every codec that ever wrote data you still keep. Retire a decoder only after confirming that no such data remains.
27A Practical Checklist Before You Choose
Walk through these questions in order and the right answer will usually appear on its own.
Who must read this data?
If unknown or outside your control, favor gzip. If only your own systems read it, you have freedom to choose anything.
How often is it read?
Many reads mean decompression speed matters greatly. Few reads mean you can accept slower reads for a smaller size.
What is the bottleneck?
CPU-bound systems need lighter codecs. Storage-bound or network-bound systems benefit from stronger ones.
How big are the records?
Tiny independent records call for batching or a dictionary. Large chunks compress well by themselves.
How long is it kept?
Long retention rewards a smaller size, since the savings repeat every month the data stays around.
What does the data contain?
If secrets mix with untrusted input, think about side-channel risk. If it is already compressed, skip compression.
Can every tool decode it?
Check clients, libraries, reporting tools, and backups. Support for newer codecs is not always universal.
How will you change your mind later?
Record the codec with the data and keep old decoders, so a future migration is a plan, not an emergency.
Choosing a codec is like choosing a way to send a package. You ask how fast it must arrive, how much you can spend, whether the receiver can accept that kind of delivery, and whether the contents are fragile. There is no single best courier, only the best one for this particular package.
When two or more answers conflict, choose zstd at a moderate level for internal systems, gzip for anything public, and LZ4 for anything where latency is the top priority. Then measure, and adjust with evidence.
28Four Worked Scenarios
Walking through realistic situations shows how the ideas from earlier chapters combine into decisions.
Scenario one: an event pipeline with tiny JSON messages
A company collects click events from millions of phones. Each event is a small JSON document of about three hundred bytes, and many share the same field names and similar values. Compressing each event alone would gain little, since there is almost nothing to reuse inside a single tiny document. The team first groups events into batches before sending them, which lets the codec find repeated field names across many events. Then they train a zstd dictionary from a sample of real events, so even small batches compress well. They pick a low zstd level to keep phone battery and server CPU costs modest. The result is far less network traffic, and the change is safe because each batch carries the identifier of the dictionary that produced it.
Scenario two: a cache that must answer in microseconds
A service keeps large serialized objects in memory to avoid slow database calls. Memory is expensive, so the team wants to shrink these objects, but the whole point of the cache is speed. Any codec that slows reads would defeat the purpose. They choose LZ4, because its decompression is so quick that the added delay is tiny compared with the time saved by avoiding the database. They measure the hit latency before and after, and confirm that it barely moves, while the memory footprint drops enough to hold many more objects in the same machines.
Scenario three: a huge archive of old logs
A team must keep several years of logs for auditing, but nobody reads them except during rare investigations. The volume is large and the storage bill grows every month. Here speed of reading barely matters, and compression happens only once in a nightly job with plenty of spare CPU time. The team recompresses day-old logs with a high zstd level using multiple threads. Storage shrinks noticeably, and the extra CPU cost for the nightly job is small compared with the monthly saving. During an audit, they can still decompress a day of logs in a short time, because zstd decompression stays fast even for heavily compressed data.
Scenario four: a public website with heavy scripts
An online shop notices that mobile visitors on slow connections leave before the page finishes loading. The scripts and style sheets are large text files that change only when the team releases a new version. The team compresses these files once during every release at the strongest setting that both gzip and newer browser-supported codecs allow, and the web server hands the small precompressed file to each visitor. Product pages, which are generated for each request, use a lighter setting so the server keeps up during busy sales. Images are left untouched since they are already compressed. Pages load noticeably faster, servers stay calm under load, and older browsers still receive gzip, so nobody is left with a broken page.
| Scenario | Main Priority | Codec and Setting | Key Trick |
|---|---|---|---|
| Click event pipeline | Network savings on tiny records | zstd low level | Batching and a trained dictionary |
| In-memory cache | Read latency | LZ4 | Measure hit latency before and after |
| Audit log archive | Lowest storage cost | zstd high level | Nightly multi-threaded recompression |
| Public shop website | Fast page loads on slow networks | gzip or Brotli, strong for static, light for dynamic | Precompress static files at release time |
In every scenario, the team started by naming their real bottleneck, chose a codec to match it, and confirmed the choice with measurements. That habit matters more than memorizing any table of codecs.
29Industry Examples and Trends
Looking at how large systems evolved shows where the industry is heading.
From Snappy Defaults to zstd Choices
For years, Snappy was the default in many big data and messaging setups because its low CPU cost was hard to beat. As zstd matured, more teams moved cold and warm data to it because it produces noticeably smaller files while decompressing quickly. Many platforms now expose zstd as a first-class option next to Snappy.
Databases Offering Multiple Choices
Storage engines commonly let operators pick different codecs for different levels of storage. Fresh data goes to LZ4 or Snappy for quick reads and writes, while deeper, older data goes to zstd to save space.
Package and Kernel Ecosystems
Several operating systems and package managers adopted zstd because installs decompress much faster than with older formats, while packages remain smaller than with simple fast codecs.
The Web Keeps gzip, and Adds More
The web still relies heavily on gzip for compatibility, and it has also added newer choices such as Brotli, and more recently zstd, where browsers and servers both support them. The trend is toward smaller pages without giving up wide reach.
| Scenario | Sensible Choice | Reason |
|---|---|---|
| Public website assets | gzip, or Brotli where supported | Broad client support |
| Real-time message batches | LZ4 or zstd low level | Low latency and CPU |
| Analytics files in a data lake | zstd or Snappy | Good balance for repeated scans |
| Long-term log archive | zstd high level or gzip | Smallest size, written once |
| In-memory cache values | LZ4 | Very fast reads |
| Small JSON records at scale | zstd with a dictionary | Dictionary rescues tiny payloads |
30Frequently Asked Questions
In most measurements zstd gives a better size, better speed, or both, so it is often the better technical choice. But gzip is understood by nearly every system on earth. If the other side cannot read zstd, gzip is the right answer no matter how good zstd looks on paper.
Snappy skips the entropy coding stage and searches for repeats quickly and lightly. It gives up some size to gain a lot of speed. That was a deliberate design choice, not a flaw.
No. Once data has been compressed, the patterns are gone, so a second pass finds almost nothing and may even make the result slightly larger. Choose one good pass instead.
Usually not by much. Higher levels mostly make the compressing side slower and hungrier for memory. Decompression speed stays similar across levels in modern codecs like zstd.
Your data may already be compressed, encrypted, or close to random. Images, videos, archives, and ciphertext have very little pattern left for a codec to find.
A dictionary is a shared block of typical content that both writer and reader know ahead of time. It helps most when compressing many tiny records, where each record alone has too little material to find repeats. It adds the work of training and versioning it, so use it when the savings justify that work.
It depends on the bottleneck. If your system waits on disk or network, spending a little CPU to move fewer bytes usually speeds things up. If CPU is already the limit, a heavy codec can slow you down, and a lighter one like LZ4 is a better fit.
Store the codec name with each block, update all readers to understand the new codec first, then switch the writers. Keep support for the old codec until all old data has expired or been rewritten.
It can be, but order and content matter. Compress first and then encrypt, since encrypted data does not compress. Be careful not to mix secrets with attacker-controlled content in the same compressed block, because size differences can leak information.
If both sides support it, start with zstd at its default level. It is fast, small, and flexible. If you need maximum compatibility, choose gzip. If speed is the only thing that matters, choose LZ4.
31Summary and Key Takeaways
Compression trades a little computing effort for a lot of savings in storage and bandwidth. All four codecs in this guide are lossless and built on the same core idea of replacing repeated data with short references. They differ in how much effort they spend. gzip is the universal classic. Snappy and LZ4 are built for raw speed and skip the entropy stage. zstd combines both stages in a modern design and offers one adjustable family that covers almost every need. The best choice always comes from understanding your bottleneck, your data, and how often it is read, and then measuring rather than guessing.
Key Takeaways
- Compression removes repeats — codecs find repeated patterns and replace them with short references, then optionally encode what is left compactly.
- gzip means compatibility — it offers a decent ratio at modest speed and is readable nearly everywhere.
- Snappy and LZ4 mean speed — they skip entropy coding to stay fast, with LZ4 especially strong on decompression.
- zstd is the flexible modern default — it can be tuned from very fast to very small and usually beats gzip on both size and speed.
- Decompression speed often matters most — data is usually written once and read many times, so read cost is paid over and over.
- Measure on your own data — benchmarks are hints, and real ratios depend on the shape of your data.
- Mind safety — limit decompressed size, avoid mixing secrets with untrusted text before encryption, and record the codec with every block.