Cassandra Tombstones
Tombstones are deletion markers in Cassandra. Because SSTables are immutable, deleted data cannot be removed immediately—a tombstone is written to mark data as deleted, which is applied during reads and removed during compaction.
Tombstone Types
Section titled “Tombstone Types”Cell Tombstone
Section titled “Cell Tombstone”Deletes a single column value.
DELETE email FROM users WHERE user_id = 123;Creates a tombstone for the email column in the specified row.
Row Tombstone
Section titled “Row Tombstone”Deletes an entire row (all columns for a clustering key).
DELETE FROM events WHERE user_id = 123 AND event_id = 456;Creates a tombstone for the entire row identified by the clustering key.
Range Tombstone
Section titled “Range Tombstone”Deletes a range of rows within a partition.
DELETE FROM messagesWHERE conversation_id = 'abc' AND sent_at >= '2024-01-01' AND sent_at < '2024-02-01';Creates a single tombstone covering all rows in the specified clustering key range.
Partition Tombstone
Section titled “Partition Tombstone”Deletes an entire partition.
DELETE FROM users WHERE user_id = 123;If user_id is the partition key, this creates a partition tombstone affecting all rows in the partition.
TTL Tombstone
Section titled “TTL Tombstone”Created automatically when a TTL expires.
INSERT INTO sessions (id, data) VALUES ('xyz', '...') USING TTL 3600;After 3600 seconds, each cell becomes a tombstone.
Tombstone Lifecycle
Section titled “Tombstone Lifecycle”GC Grace Period
Section titled “GC Grace Period”The gc_grace_seconds setting determines how long tombstones are preserved before removal.
Default Value
Section titled “Default Value”-- Default: 10 days (864000 seconds)SELECT gc_grace_seconds FROM system_schema.tablesWHERE keyspace_name = 'ks' AND table_name = 'table';GC Grace Period Implications
Section titled “GC Grace Period Implications”Scenario: Node C is down during a DELETE (RF=3)
Before DELETE: Node A: user_id=123 → "Alice" Node B: user_id=123 → "Alice" Node C: user_id=123 → "Alice" [OFFLINE]
DELETE user WHERE user_id = 123 (CL=QUORUM)
After DELETE: Node A: user_id=123 → TOMBSTONE Node B: user_id=123 → TOMBSTONE Node C: user_id=123 → "Alice" [Still has old data]
If Node C returns AFTER gc_grace_seconds: - Tombstones on A and B may have been compacted away - Node C still has "Alice" - Anti-entropy repair or read (if read repair enabled) sees inconsistency - "Alice" gets RESURRECTED (zombie data)Configuration Guidelines
Section titled “Configuration Guidelines”| Scenario | gc_grace_seconds | Repair Frequency |
|---|---|---|
| Default | 864000 (10 days) | Weekly |
| Frequent repair | 172800 (2 days) | Daily |
| Time-series with TTL | 86400 (1 day) | Daily |
| High churn (careful) | 3600 (1 hour) | Hourly |
-- Adjust per tableALTER TABLE my_table WITH gc_grace_seconds = 172800;Rule: gc_grace_seconds must exceed maximum expected node downtime plus repair interval.
Tombstone Configuration
Section titled “Tombstone Configuration”Warning and Failure Thresholds
Section titled “Warning and Failure Thresholds”# Warn when query scans this many tombstonestombstone_warn_threshold: 1000
# Fail query when this many tombstones scannedtombstone_failure_threshold: 100000When exceeded:
WARN: Read X live rows and Y tombstone cells for query...ERROR: Scanned over 100000 tombstones; query abortedTombstone Problems
Section titled “Tombstone Problems”Problem 1: Tombstone Accumulation
Section titled “Problem 1: Tombstone Accumulation”Symptoms:
- Read latency increasing over time
- “Read X live rows and Y tombstone cells” warnings
- Query timeouts on specific partitions
Causes:
- Deleting many rows without compaction
- Wide partitions with frequent deletes
- Range deletes creating overlapping tombstones
Investigation:
# Check tombstone warningsgrep "tombstone" /var/log/cassandra/system.log
# Table statisticsnodetool tablestats keyspace.table | grep -i tombstone
# Per-SSTable tombstone analysistools/bin/sstablemetadata /path/to/na-*-Data.db | grep -i tombstoneProblem 2: Query Failures
Section titled “Problem 2: Query Failures”Error:
Scanned over 100000 tombstones; query abortedSolutions (in order of preference):
- Fix data model to avoid tombstone accumulation
- Force compaction to remove eligible tombstones
- Add time-based partitioning to limit partition size
- Increase threshold (last resort—hides the problem)
Problem 3: Partition Tombstones with Wide Partitions
Section titled “Problem 3: Partition Tombstones with Wide Partitions”Bad pattern:
Partition: user_id=123├── Row: event_1 → data├── Row: event_2 → data├── ... 100,000 rows ...└── Row: event_100000 → data
DELETE FROM events WHERE user_id = 123;
Creates ONE partition tombstone, but on read, must checkagainst ALL rows in all SSTables = massive read amplificationBetter pattern:
-- Partition by user_id AND date-- Delete smaller partitionsDELETE FROM eventsWHERE user_id = 123 AND event_date = '2024-01-15';Monitoring Tombstones
Section titled “Monitoring Tombstones”nodetool Commands
Section titled “nodetool Commands”# Table statistics including tombstone infonodetool tablestats keyspace.table
# Tombstones per read histogramnodetool tablehistograms keyspace.tableJMX Metrics
Section titled “JMX Metrics”org.apache.cassandra.metrics:type=Table,name=TombstoneScannedHistogramorg.apache.cassandra.metrics:type=Table,name=LiveScannedHistogramSSTable Analysis
Section titled “SSTable Analysis”# Check tombstone counts per SSTablefor f in /var/lib/cassandra/data/ks/table-*/*-Data.db; do echo "=== $f ===" tools/bin/sstablemetadata "$f" | grep -i tombstonedoneReducing Tombstones
Section titled “Reducing Tombstones”Data Model Changes
Section titled “Data Model Changes”-
Avoid wide partitions with deletes
- Add time bucketing to partition key
- Limit partition size
-
Use TTL instead of explicit deletes
- TTL tombstones are more predictable
- Easier to reason about cleanup
-
Avoid range deletes on large ranges
- Delete smaller ranges
- Use time-based partitioning
Compaction Strategies
Section titled “Compaction Strategies”TWCS (Time-Window Compaction Strategy):
Best for time-series data with TTL. Entire SSTables drop when all data expires.
ALTER TABLE metrics WITH compaction = { 'class': 'TimeWindowCompactionStrategy', 'compaction_window_size': 1, 'compaction_window_unit': 'DAYS'};LCS (Leveled Compaction Strategy):
Keeps SSTable count low, improving tombstone cleanup.
ALTER TABLE events WITH compaction = { 'class': 'LeveledCompactionStrategy'};Manual Compaction
Section titled “Manual Compaction”Force compaction to remove eligible tombstones:
# Compact specific tablenodetool compact keyspace table
# Major compaction (use sparingly)nodetool compact --user-defined /path/to/sstablesTombstone Best Practices
Section titled “Tombstone Best Practices”Design
Section titled “Design”- Partition by time for time-series data
- Keep partitions bounded in size
- Prefer TTL over explicit deletes when possible
Operations
Section titled “Operations”- Run repair within gc_grace_seconds
- Monitor tombstone counts per read
- Investigate tables with high tombstone warnings
Configuration
Section titled “Configuration”- Set gc_grace_seconds based on repair frequency
- Set tombstone thresholds appropriately
- Use TWCS for TTL-heavy workloads
Related Documentation
Section titled “Related Documentation”- Storage Engine Overview - Architecture overview
- Read Path - How tombstones affect reads
- Compaction - Tombstone removal