Skip to content

AxonOps — AI-Native Control Plane for Open Source Data Platforms

Cassandra SSTable Compression

Cassandra compresses SSTable data at the file level using a chunk-based scheme. Each SSTable's data file is divided into fixed-size blocks, each compressed independently. This reduces disk I/O and storage consumption at the cost of CPU cycles during reads (decompression) and writes (compression).

Compression is enabled by default on all tables using LZ4Compressor.

SSTable Compression vs Protocol Compression

This page covers SSTable compression—compression of data at rest on disk. For compression of CQL protocol frames in transit between clients and nodes, see Protocol Compression.


SSTable data is divided into fixed-size uncompressed chunks (controlled by chunk_length_in_kb, default 16 KB in Cassandra 4.0+). Each chunk is compressed independently and followed by a 4-byte Adler32 checksum.

Uncompressed data: [Chunk 0: 16 KB][Chunk 1: 16 KB][Chunk 2: 16 KB][Chunk 3: 16 KB]
↓ ↓ ↓ ↓
Compressed on disk: [~6 KB + CRC] [~7 KB + CRC] [~5 KB + CRC] [~8 KB + CRC]

The CompressionInfo.db component file stores the byte offset of each compressed chunk within the Data.db file. This offset table enables random access: to read data at a given uncompressed position, Cassandra calculates the chunk number and looks up its compressed offset. For details on the CompressionInfo.db file format, see SSTable Components.

Independent chunk compression means:

  • Any single chunk can be decompressed without reading or decompressing adjacent chunks
  • Writes append compressed chunks sequentially—there is no need to rewrite the entire file
  • Corruption in one chunk does not affect decompression of other chunks

When a read request requires data from a compressed SSTable:

  1. The coordinator identifies the relevant SSTable and the row's position within the uncompressed data
  2. The chunk number is calculated: chunk_number = position / chunk_length
  3. The compressed chunk offset is looked up in the CompressionInfo.db offset table
  4. The entire chunk is read from disk and decompressed—partial chunk decompression is not possible
  5. The CRC checksum is verified with probability equal to crc_check_chance
  6. The requested row is extracted from the decompressed chunk

This creates read amplification: even if only a single row (e.g., 200 bytes) is needed, the full chunk (e.g., 16 KB uncompressed) must be read and decompressed. Chunk size tuning directly controls this tradeoff. See Chunk Size Tuning.

For the full read path, see Read Path.

Compression is applied when SSTables are written during memtable flushes and compaction. Key behavioral properties:

  • Changing compression settings via ALTER TABLE affects only newly written SSTables. Existing SSTables retain their original compression until rewritten by compaction or explicit upgrade.
  • To force recompression of existing SSTables, use nodetool upgradesstables or nodetool recompress_sstables.
  • The flush_compression setting in cassandra.yaml (Cassandra 4.1+, CASSANDRA-15379) controls whether flushed SSTables use a fast compressor or the table-configured compressor. This allows faster flushes at the cost of temporarily lower compression ratios, with compaction applying the table's configured compressor later.

Compression metadata (the chunk offset table) is stored off-heap, outside the Java heap. This is important for capacity planning:

chunk_length_in_kbChunks per GB (uncompressed)Relative Metadata Overhead
4~262,14416x
8~131,0728x
16~65,5364x
32~32,7682x
64~16,3841x (baseline)
128~8,1920.5x
256~4,0960.25x

As a rough estimate, compression metadata consumes approximately 1–3 GB of off-heap memory per TB of on-disk data, depending on chunk size and compression ratio. Monitor this with nodetool tablestats (the Compression metadata off heap memory used field).


CompressorAvailable SinceCompression SpeedDecompression SpeedTypical RatioCPU CostPrimary Use Case
LZ4Compressor1.2.2 (default since 2.0)FastestFastest~2–2.5xLowestGeneral purpose, latency-sensitive workloads
SnappyCompressor1.0FastFast~2–2.5xLowLegacy; LZ4 generally preferred
ZstdCompressor4.0MediumFast~3–4xMediumStorage-constrained deployments
DeflateCompressor1.0SlowMedium~3–4xHighArchival or cold data
NoopCompressor4.0N/AN/A1xNonePre-compressed or high-entropy data

LZ4 is the default compressor for all tables since Cassandra 2.0. It provides the fastest compression and decompression among all available algorithms, with moderate compression ratios.

Fast mode (default): Optimized for speed with a fixed compression acceleration.

High-compression mode (Cassandra 3.6+, CASSANDRA-11051): Achieves better compression ratios at the cost of slower compression. Decompression speed is unaffected.

-- LZ4 fast mode (default)
CREATE TABLE sensor_data (...) WITH compression = {
'class': 'LZ4Compressor'
};
-- LZ4 high-compression mode
ALTER TABLE archival_data WITH compression = {
'class': 'LZ4Compressor',
'lz4_compressor_type': 'high',
'lz4_high_compressor_level': 12
};
ParameterValuesDefaultDescription
lz4_compressor_typefast, highfastSelects fast or high-compression mode
lz4_high_compressor_level1–179Compression level for high mode. Higher values produce better ratios with slower compression

Snappy was the default compressor before Cassandra 2.0. It offers similar performance characteristics to LZ4 but is retained primarily for backward compatibility. For new deployments, LZ4 is preferred as it generally provides equal or better performance across both compression and decompression.

ALTER TABLE my_table WITH compression = {
'class': 'SnappyCompressor'
};

Zstandard (Zstd) was added in Cassandra 4.0 (CASSANDRA-14482). It achieves significantly better compression ratios than LZ4—typically 30–50% smaller compressed output on structured data—while maintaining competitive decompression speeds.

ALTER TABLE large_table WITH compression = {
'class': 'ZstdCompressor',
'compression_level': 3
};
ParameterValuesDefaultDescription
compression_level-131072 to 223Higher values improve compression ratio at the cost of compression speed. Negative values enable fast mode with progressively less compression

Zstd and Storage Savings

On workloads with structured or repetitive data (JSON, log entries, wide rows with similar columns), Zstd at its default level can reduce SSTable size by 30–50% compared to LZ4, with minimal impact on read latency since decompression speed remains fast.

Deflate provides the highest compression ratios among the standard compressors but at significantly higher CPU cost for both compression and decompression. It is suitable for cold or archival data where storage savings outweigh latency concerns.

ALTER TABLE cold_data WITH compression = {
'class': 'DeflateCompressor'
};

Compression can be disabled entirely or replaced with NoopCompressor:

-- Disable compression
ALTER TABLE my_table WITH compression = {'enabled': 'false'};
-- NoopCompressor (4.0+): maintains metadata structure without compressing
ALTER TABLE my_table WITH compression = {'class': 'NoopCompressor'};

Disabling compression is appropriate when:

  • Data is already in a compressed format (JPEG, PNG, video, compressed archives)
  • Data has high entropy (encrypted data, random byte sequences)
  • The workload requires absolute minimum read latency and sufficient disk capacity exists
  • Off-heap memory is severely constrained and the dataset is large

Compression is configured per table using the WITH compression clause in CREATE TABLE or ALTER TABLE:

CREATE TABLE my_keyspace.my_table (
id uuid PRIMARY KEY,
data text
) WITH compression = {
'class': 'ZstdCompressor',
'chunk_length_in_kb': 16,
'crc_check_chance': 1.0,
'compression_level': 3
};
ParameterTypeDefaultDescription
classstringLZ4CompressorFully qualified or short class name of the compressor
chunk_length_in_kbinteger16 (4.0+), 64 (pre-4.0)Uncompressed chunk size in KB. Must be a power of 2, minimum 4
crc_check_chancefloat1.0Probability (0.0–1.0) of verifying the CRC checksum on each chunk read
enabledbooleantrueSet to false to disable compression entirely
compression_levelinteger3 (Zstd)Compressor-specific compression level (ZstdCompressor only)
lz4_compressor_typestringfastLZ4 mode: fast or high (LZ4Compressor only, 3.6+)
lz4_high_compressor_levelinteger9LZ4 high-compression level, 1–17 (LZ4Compressor only, 3.6+)

To view a table's current compression settings:

SELECT compression FROM system_schema.tables
WHERE keyspace_name = 'my_keyspace' AND table_name = 'my_table';

Changing compression via ALTER TABLE only affects SSTables written after the change. To rewrite existing SSTables with the new settings:

Terminal window
# Rewrite all SSTables for a table (triggers recompression)
nodetool upgradesstables my_keyspace my_table
# Recompress without full SSTable rewrite (4.0+)
nodetool recompress_sstables my_keyspace my_table

For details, see nodetool recompress_sstables.

Operational Impact

Both upgradesstables and recompress_sstables perform disk-intensive operations. Schedule these during low-traffic periods and monitor disk I/O and compaction throughput during execution.

Several cassandra.yaml settings control compression at the server level:

SettingDefaultDescription
flush_compressiontable-configuredControls compression during memtable flushes. Options: none, fast, or the table's configured compressor (4.1+, CASSANDRA-15379)
commitlog_compressionnoneCompressor for commit log segments. Supports LZ4, Snappy, Deflate. See Commit Log
internode_compressiondcCompression for inter-node traffic. Options: all, dc (inter-DC only), none
hints_compressionnoneCompressor for hint files. Supports LZ4, Snappy, Deflate

The chunk_length_in_kb parameter is the primary tuning lever for compression performance. It controls the tradeoff between three factors:

FactorSmaller Chunks (4–16 KB)Larger Chunks (64–256 KB)
Read amplificationLower — less wasted I/O per readHigher — more data read and decompressed per request
Compression ratioLower — less data context per chunkHigher — compressor has more data to find patterns
Off-heap memoryHigher — more chunk offsets to storeLower — fewer chunk offsets
Random read latencyLowerHigher
Sequential read throughputSimilarSlightly better
Access PatternRecommended chunk_length_in_kbRationale
Random point reads4–16Minimizes read amplification; each read decompresses only a small block
Mixed read/write16Balanced tradeoff; matches Cassandra 4.0+ default
Range scans / sequential64–256Better compression ratio; sequential reads amortize decompression over many rows
Write-heavy with memory constraints64–128Reduces off-heap metadata overhead

Default Change in Cassandra 4.0

The default chunk_length_in_kb changed from 64 to 16 in Cassandra 4.0, reflecting that most production workloads are read-heavy with random access patterns. Tables created prior to 4.0 retain the 64 KB setting unless explicitly altered.


Use nodetool tablestats to inspect compression behavior per table:

Terminal window
nodetool tablestats my_keyspace.my_table

Key fields:

FieldDescription
SSTable Compression RatioRatio of compressed to uncompressed size (e.g., 0.37 means 37% of original size)
Compression metadata off heap memory usedOff-heap memory consumed by compression offset tables

A compression ratio close to 1.0 indicates the compressor provides minimal benefit for the data in that table. Consider disabling compression or switching algorithms.

ScenarioRecommended Algorithmchunk_length_in_kbRationale
General purposeLZ4Compressor16Safe default; fast compression and decompression
Read-heavy, latency-sensitiveLZ4Compressor4–16Minimizes decompression overhead and read amplification
Storage-constrainedZstdCompressor16–64Better compression ratio with competitive decompression speed
Archival / cold dataDeflateCompressor or ZstdCompressor (high level)64–256Maximum storage reduction; read latency is less critical
Pre-compressed dataDisabled or NoopCompressorAvoids double-compression overhead with no benefit
Write-heavy, memory-constrainedLZ4Compressor64Fast compression with lower off-heap overhead

Do Not Disable Without Measurement

Problem: Disabling compression without profiling wastes disk space and increases I/O without improving latency.

Instead: Use nodetool tablestats to check the compression ratio first. If the ratio is close to 1.0 (minimal compression benefit), disabling may be appropriate. If the ratio is 0.5 or lower, compression is providing significant storage and I/O savings.

Compression should be disabled only when:

  • The compression ratio is consistently near 1.0 (the data does not compress)
  • Data is pre-compressed or encrypted
  • Off-heap memory is critically constrained and cannot accommodate compression metadata

Bitrot Detection

The crc_check_chance parameter (default 1.0) is Cassandra's primary defense against silent data corruption (bitrot) in SSTables. At the default value, every chunk read verifies its CRC32 checksum.

Lowering this value reduces CPU overhead on the read path but increases the risk of serving corrupted data without detection. Only reduce crc_check_chance if performance profiling demonstrates CRC verification is a measurable bottleneck, and ensure other integrity mechanisms (e.g., filesystem checksums, hardware RAID) are in place.


VersionChangeReference
1.0SnappyCompressor and DeflateCompressor available
1.2.2LZ4Compressor added
2.0LZ4Compressor becomes the default compressor
3.6LZ4 high-compression mode added (lz4_compressor_type, lz4_high_compressor_level)CASSANDRA-11051
4.0ZstdCompressor addedCASSANDRA-14482
4.0Default chunk_length_in_kb changed from 64 to 16
4.0NoopCompressor added
4.1flush_compression cassandra.yaml setting addedCASSANDRA-15379