Skip to main content
Snapshots in RocksDB provide a consistent, point-in-time view of the database. They allow you to read data as it existed at a specific moment, even as concurrent writes continue to modify the database.

What are Snapshots?

A snapshot is an immutable reference to the database state at a specific sequence number:
  • Consistent reads: See the same data across multiple operations
  • No blocking: Concurrent writes proceed normally
  • Lightweight: Creating a snapshot is O(1)
  • Read-only: Snapshots only affect reads, not writes
Snapshots don’t copy data. They’re lightweight markers that prevent old data from being garbage collected while the snapshot exists.

Creating Snapshots

Basic Snapshot Usage

#include <rocksdb/db.h>
#include <rocksdb/snapshot.h>

rocksdb::DB* db;
rocksdb::Options options;
options.create_if_missing = true;
rocksdb::DB::Open(options, "/tmp/testdb", &db);

// Write initial data
db->Put(rocksdb::WriteOptions(), "key1", "value1");
db->Put(rocksdb::WriteOptions(), "key2", "value2");

// Create a snapshot
const rocksdb::Snapshot* snapshot = db->GetSnapshot();

// Continue writing (won't affect snapshot)
db->Put(rocksdb::WriteOptions(), "key1", "new_value");
db->Delete(rocksdb::WriteOptions(), "key2");

// Read from snapshot (sees old data)
rocksdb::ReadOptions read_opts;
read_opts.snapshot = snapshot;

std::string value;
db->Get(read_opts, "key1", &value); // Returns "value1"
db->Get(read_opts, "key2", &value); // Returns "value2"

// Release snapshot when done
db->ReleaseSnapshot(snapshot);
Always release snapshots when finished. Holding snapshots prevents compaction from removing old data, increasing space usage.

Managed Snapshots (RAII)

Use ManagedSnapshot for automatic cleanup:
#include <rocksdb/snapshot.h>

{
  // Snapshot automatically created
  rocksdb::ManagedSnapshot snapshot(db);
  
  // Read from snapshot
  rocksdb::ReadOptions read_opts;
  read_opts.snapshot = snapshot.snapshot();
  
  std::string value;
  db->Get(read_opts, "key", &value);
  
  // Snapshot automatically released when out of scope
}

Snapshot Properties

Sequence Numbers

Each write operation increments the database sequence number:
const rocksdb::Snapshot* snapshot = db->GetSnapshot();

// Get the sequence number
rocksdb::SequenceNumber seq = snapshot->GetSequenceNumber();
std::cout << "Snapshot at sequence: " << seq << std::endl;

// Get timestamp (Unix time in seconds)
int64_t unix_time = snapshot->GetUnixTime();
std::cout << "Snapshot time: " << unix_time << std::endl;
  • Every write operation (Put, Delete, Merge) gets a unique sequence number
  • Sequence numbers are monotonically increasing
  • A snapshot captures the sequence number at creation time
  • Reads with a snapshot only see data with sequence numbers ≤ snapshot sequence

Use Cases

Consistent Backups

Create consistent backups without blocking writes:
#include <rocksdb/db.h>
#include <fstream>

// Create snapshot for backup
const rocksdb::Snapshot* snapshot = db->GetSnapshot();

try {
  rocksdb::ReadOptions read_opts;
  read_opts.snapshot = snapshot;
  
  // Iterate entire database consistently
  rocksdb::Iterator* it = db->NewIterator(read_opts);
  
  std::ofstream backup("backup.txt");
  for (it->SeekToFirst(); it->Valid(); it->Next()) {
    backup << it->key().ToString() << "\t" 
           << it->value().ToString() << "\n";
  }
  
  delete it;
  backup.close();
  
} catch (...) {
  db->ReleaseSnapshot(snapshot);
  throw;
}

db->ReleaseSnapshot(snapshot);

Multi-Read Consistency

Ensure multiple reads see consistent data:
// Create snapshot
const rocksdb::Snapshot* snapshot = db->GetSnapshot();
rocksdb::ReadOptions read_opts;
read_opts.snapshot = snapshot;

// Read user and their balance
std::string user_data, balance;
db->Get(read_opts, "user:1001", &user_data);
db->Get(read_opts, "balance:1001", &balance);

// Values are consistent even if concurrent writes occur
// Both reads see the same snapshot of the database

db->ReleaseSnapshot(snapshot);
Without snapshots, concurrent writes between the two reads could lead to inconsistent data (e.g., reading old user data but new balance).

Range Scan Consistency

Scan ranges without interference from concurrent writes:
const rocksdb::Snapshot* snapshot = db->GetSnapshot();
rocksdb::ReadOptions read_opts;
read_opts.snapshot = snapshot;

rocksdb::Iterator* it = db->NewIterator(read_opts);

// Consistent range scan
for (it->Seek("user:1000"); it->Valid(); it->Next()) {
  if (it->key().ToString() > "user:2000") break;
  
  // Process user data consistently
  // No new users appear mid-iteration
  // No users disappear mid-iteration
  ProcessUser(it->key(), it->value());
}

delete it;
db->ReleaseSnapshot(snapshot);

Snapshots and Column Families

Snapshots work across all column families:
std::vector<rocksdb::ColumnFamilyHandle*> handles;
// ... open database with column families ...

// Single snapshot covers all column families
const rocksdb::Snapshot* snapshot = db->GetSnapshot();

rocksdb::ReadOptions read_opts;
read_opts.snapshot = snapshot;

// Read from different column families consistently
std::string user, post;
db->Get(read_opts, handles[0], "user:1001", &user);
db->Get(read_opts, handles[1], "post:5001", &post);

// Both reads see the same point-in-time across all CFs

db->ReleaseSnapshot(snapshot);
A single snapshot provides consistency across ALL column families in the database.

Snapshots and Compaction

Snapshots prevent data from being removed:
// Write data
db->Put(rocksdb::WriteOptions(), "key1", "value1");

// Create snapshot
const rocksdb::Snapshot* snapshot = db->GetSnapshot();

// Update key (creates new version)
db->Put(rocksdb::WriteOptions(), "key1", "value2");

// Compaction runs but CANNOT remove "value1"
// because snapshot still references it

// Release snapshot
db->ReleaseSnapshot(snapshot);

// Now "value1" can be garbage collected during compaction
Important: Long-lived snapshots increase space amplification. They prevent compaction from removing old versions of keys, causing disk space usage to grow.

Iterator Snapshots

Iterators implicitly use snapshots:
rocksdb::ReadOptions read_opts;
// No snapshot specified - iterator creates implicit snapshot

rocksdb::Iterator* it = db->NewIterator(read_opts);

// Iterator sees consistent snapshot from creation time
for (it->SeekToFirst(); it->Valid(); it->Next()) {
  // Consistent iteration even if concurrent writes occur
}

delete it;
// Implicit snapshot released when iterator destroyed

Explicit vs Implicit Snapshots

Create snapshot first, then use it:
const rocksdb::Snapshot* snapshot = db->GetSnapshot();

rocksdb::ReadOptions read_opts;
read_opts.snapshot = snapshot;

// Multiple iterators share same snapshot
rocksdb::Iterator* it1 = db->NewIterator(read_opts);
rocksdb::Iterator* it2 = db->NewIterator(read_opts);

// Both see identical data

delete it1;
delete it2;
db->ReleaseSnapshot(snapshot);

Monitoring Snapshots

List Active Snapshots

std::vector<uint64_t> snapshot_seqs;
db->GetAllSnapshots(&snapshot_seqs);

std::cout << "Active snapshots: " << snapshot_seqs.size() << std::endl;
for (uint64_t seq : snapshot_seqs) {
  std::cout << "  Sequence: " << seq << std::endl;
}

Check Oldest Snapshot

std::string value;
db->GetProperty("rocksdb.oldest-snapshot-sequence", &value);
std::cout << "Oldest snapshot sequence: " << value << std::endl;

// If oldest snapshot is very old, it may be preventing compaction

Performance Considerations

  • Time: O(1) - just records current sequence number
  • Space: Negligible - a few bytes per snapshot
  • Overhead: No impact on write performance
Creating snapshots is very cheap.
  • Same as normal reads if data is still in cache
  • May require reading old data from disk if not cached
  • No blocking - reads from snapshots don’t block writes
Reading from snapshots is efficient.
  • Prevents compaction from removing old data
  • Increases space amplification over time
  • More disk I/O as obsolete data accumulates
Release snapshots promptly to minimize impact.

Best Practices

  1. Release snapshots promptly - Don’t hold them longer than necessary
  2. Use ManagedSnapshot for automatic cleanup with RAII
  3. Monitor snapshot age - Watch for leaked snapshots
  4. Snapshots for consistency - Use when multiple reads must be consistent
  5. Implicit snapshots - Let iterators create snapshots when appropriate
  6. Avoid long-lived snapshots - They prevent compaction and increase space
  7. Backup with snapshots - Ensures consistent backup without blocking writes

Comparison with Transactions

FeatureSnapshotsTransactions
ReadsConsistent point-in-timeConsistent within transaction
WritesNot supportedSupported with rollback
IsolationSnapshot isolationConfigurable isolation
OverheadVery lowHigher (write tracking)
Use CaseConsistent readsACID semantics
Use snapshots for consistent reads. Use transactions (from RocksDB utilities) when you need atomic multi-key updates with rollback capability.

Example: Consistent Multi-Step Query

#include <rocksdb/db.h>
#include <vector>

// Query all posts by a user and their details
std::vector<std::string> GetUserPosts(rocksdb::DB* db, 
                                      const std::string& user_id) {
  std::vector<std::string> posts;
  
  // Create snapshot for consistency
  rocksdb::ManagedSnapshot snapshot(db);
  rocksdb::ReadOptions read_opts;
  read_opts.snapshot = snapshot.snapshot();
  
  // Get user's post list
  std::string post_list;
  rocksdb::Status s = db->Get(read_opts, "user_posts:" + user_id, &post_list);
  
  if (!s.ok()) return posts;
  
  // Parse post IDs
  std::vector<std::string> post_ids = ParsePostIds(post_list);
  
  // Fetch each post (all consistent with snapshot)
  for (const auto& post_id : post_ids) {
    std::string post_data;
    s = db->Get(read_opts, "post:" + post_id, &post_data);
    if (s.ok()) {
      posts.push_back(post_data);
    }
  }
  
  return posts;
  // Snapshot automatically released
}

Next Steps

Write-Ahead Log

Understand durability with WAL

Architecture

See how snapshots fit in RocksDB

Transactions

Use transactions for atomic updates

Backup & Restore

Create consistent backups with snapshots

Build docs developers (and LLMs) love