Cassandra Caching

Cassandra provides two built-in caches that live on each node: the key cache and the row cache. These caches store frequently accessed data in memory, reducing or eliminating disk reads for hot data. Configuring them correctly can dramatically reduce read latency for workloads with predictable access patterns.

The Two Caches

Cache          What It Stores                    Default
──────────────────────────────────────────────────────────────
Key Cache      Partition key → SSTable byte       Enabled
               offset mapping                    (5% of heap)
Row Cache      Entire row data                    Disabled by default
               (full partition content)

Key Cache

The key cache stores the exact byte offset of a partition key within its SSTable. When Cassandra needs to read a partition, it first checks the key cache. A cache hit means Cassandra can seek directly to the right position in the SSTable file, skipping the Bloom filter check and partition index lookup.

Without key cache:
  Read request
    → Bloom filter check (in memory)
    → Partition summary (in memory)
    → Partition index (disk read)
    → Seek to data (disk read)
    → Read row (disk read)

With key cache hit:
  Read request
    → Key cache hit → "partition X is at byte offset 14,523,392"
    → Seek to offset (disk read)
    → Read row (disk read)

Key cache saves: Bloom filter + partition index reads

Configuring Key Cache (Global)

# cassandra.yaml
key_cache_size_in_mb: 100      # default: 5% of heap or 100MB max
key_cache_save_period: 14400   # save to disk every 4 hours (seconds)
key_cache_keys_to_save: 0      # 0 = save all cached keys

Configuring Key Cache Per Table

-- Enable key caching for all keys on a specific table:
ALTER TABLE hot_products
  WITH caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'};

-- Disable key cache for a cold archive table:
ALTER TABLE cold_archive
  WITH caching = {'keys': 'NONE', 'rows_per_partition': 'NONE'};

Row Cache

The row cache stores complete partition data (all rows in a partition) in memory. A row cache hit means zero disk I/O for the entire read — the result comes straight from memory. This makes the row cache extremely effective for tables with small partitions that are read very frequently.

Without row cache:
  Read → Bloom filter → Key cache → SSTable data (disk)

With row cache hit:
  Read → Row cache → result returned (zero disk I/O)

When Row Cache Is Effective

Good fit for row cache:
  ✓ Table has small partitions (< a few MB each)
  ✓ A small number of "hot" partitions are read repeatedly
  ✓ Read-heavy workload with low write rate
  ✓ Data changes infrequently

Poor fit for row cache:
  ✗ Large partitions (entire partition cached in RAM)
  ✗ High write rate (writes invalidate cached rows constantly)
  ✗ Millions of distinct partitions (cache thrashes)
  ✗ Time-series data (each read is a different time range)

Configuring Row Cache (Global)

# cassandra.yaml
row_cache_size_in_mb: 0        # default: 0 (disabled)
row_cache_save_period: 0       # save to disk (0 = disabled)

To enable the row cache, set row_cache_size_in_mb to a non-zero value. Start conservatively — 512 MB to 2 GB — and monitor the hit rate before increasing.

row_cache_size_in_mb: 1024     # 1 GB for row cache
row_cache_save_period: 14400   # save snapshot every 4 hours

Configuring Row Cache Per Table

-- Cache every row in every partition:
ALTER TABLE user_profiles
  WITH caching = {'keys': 'ALL', 'rows_per_partition': 'ALL'};

-- Cache only the 100 most recent rows per partition:
ALTER TABLE messages
  WITH caching = {'keys': 'ALL', 'rows_per_partition': '100'};

-- Disable row cache for this table:
ALTER TABLE audit_log
  WITH caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'};

Cache Interaction with Writes

Every write to a partition invalidates the row cache entry for that partition. If a table receives many writes, the row cache constantly discards entries before they can be reused, making caching ineffective. Monitor the cache hit rate — if it is low, the row cache is wasting memory rather than helping.

Write invalidation timeline:

10:00 Read partition X → cached in row cache
10:01 Write to partition X → row cache entry INVALIDATED
10:01 Read partition X → cache miss → disk read → re-cached
10:01 Write to partition X again → INVALIDATED again
...

High write rate → row cache is useless for this partition

Monitoring Cache Performance

nodetool info

Key Cache              : entries 12345, size 45.2 MB, capacity 100 MB,
                         91 hits, 9 requests, 0.91 recent hit rate
Row Cache              : entries 5432,  size 320 MB, capacity 1024 MB,
                         78 hits, 100 requests, 0.78 recent hit rate

Interpreting Hit Rates

Hit Rate    Interpretation          Action
──────────────────────────────────────────────────────────────
> 0.85      Excellent               Cache is earning its memory
0.60–0.85   Good                    Monitor; consider more memory
0.30–0.60   Moderate                Evaluate whether table fits cache
< 0.30      Poor                    Disable cache for this table;
                                    memory wasted on cache thrash

Saving and Loading Caches on Restart

Cassandra saves a snapshot of the key and row caches to disk periodically. On restart, it warms the cache from this snapshot, avoiding a cold-cache period where every read misses until the cache fills again.

# Manually save caches:
nodetool setcachecapacity 100 1024  # set key cache MB, row cache MB
nodetool savecaches

# Force cache warmup after restart:
nodetool invalidatekeycache         # clear key cache
nodetool invalidaterowcache         # clear row cache

Summary

The key cache stores partition offsets and eliminates index lookups on repeated reads — it is enabled by default and benefits almost all workloads. The row cache stores complete partition data and eliminates all disk I/O on cache hits, but only helps for tables with small, frequently read, rarely written partitions. Configure caching per table using the caching property. Monitor hit rates with nodetool and disable the row cache for tables where the hit rate stays below 60%.

Leave a Comment

Your email address will not be published. Required fields are marked *