RocksDB-Sharp

Column families

A column family (CF) is a logical key space inside a single RocksDB database — think "table". Every database has at least one column family called "default". You can create more to:

  • Keep keys with different schemas separate (no prefix collisions).
  • Tune compaction, compression, or block size per-namespace.
  • Drop a whole namespace cheaply with DropColumnFamily.
  • Atomically write across multiple namespaces — a single WriteBatch can touch any column family.

Upstream reference: Column Families wiki.


Listing existing column families

foreach (var name in RocksDb.ListColumnFamilies(new DbOptions(), path))
{
    Console.WriteLine(name);
}

A non-throwing variant returns a bool:

if (RocksDb.TryListColumnFamilies(new DbOptions(), path, out var names))
{
    // … use names
}

Opening with column families

Every Open call must enumerate every existing column family — RocksDB throws if any are missing.

var cfs = new ColumnFamilies
{
    { "users",   new ColumnFamilyOptions() },
    { "orders",  new ColumnFamilyOptions().SetCompression(Compression.Zstd) },
};

var options = new DbOptions()
    .SetCreateIfMissing(true)
    .SetCreateMissingColumnFamilies(true);

using var db = RocksDb.Open(options, path, cfs);

ColumnFamilies always carries a "default" family. You can override its options:

cfs.Add(new ColumnFamilies.Descriptor(
    ColumnFamilies.DefaultName,
    new ColumnFamilyOptions().SetCompression(Compression.Lz4)));

ColumnFamilyOptions reference


Getting handles

A ColumnFamilyHandle is the runtime token for "this CF". Every read/write API takes one as an optional last argument; null means the default CF.

var users  = db.GetColumnFamily("users");
var orders = db.GetColumnFamily("orders");
var def    = db.GetDefaultColumnFamily();

db.Put("u:42", "Ada", users);
string name = db.Get("u:42", users);

The safe variant returns false if the CF is missing:

if (db.TryGetColumnFamily("audit", out var audit))
{
    db.Put("e:1", "...", audit);
}

Creating and dropping at runtime

var audit = db.CreateColumnFamily(new ColumnFamilyOptions(), "audit");
db.Put("e:1", "...", audit);

db.DropColumnFamily("audit");

Dropping is cheap — RocksDB tombstones the whole CF and compaction reclaims the space asynchronously.


Atomic writes across families

A single WriteBatch can touch any number of column families. The whole batch is one atomic write.

using var batch = new WriteBatch();
batch.Put("o:42", "pending", orders);
batch.Put("u:42", "Ada",     users);
batch.Delete("draft:42",     users);

db.Write(batch);   // atomic across families

Iterating a single column family

Pass the handle to NewIterator:

using var it = db.NewIterator(cf: users);
for (it.SeekToFirst(); it.Valid(); it.Next())
{
    Console.WriteLine($"{it.StringKey()} = {it.StringValue()}");
}

Per-family tuning

Each ColumnFamilyOptions is independent — different CFs can have different compaction styles, compression algorithms, block sizes, or merge operators.

var hot = new ColumnFamilyOptions()
    .SetCompression(Compression.No)
    .SetWriteBufferSize(64UL * 1024 * 1024);

var cold = new ColumnFamilyOptions()
    .SetCompression(Compression.Zstd)
    .OptimizeForPointLookup(blockCacheSizeMb: 64);

var cfs = new ColumnFamilies
{
    { "hot",  hot  },
    { "cold", cold },
};

See the Tuning Guide for which knobs are CF-scoped vs. DB-wide.


Common patterns

Schemas as CFs

One CF per logical type — users, orders, events. No key-prefix collisions, easy to drop a type entirely.

Hot / warm / cold

Split data by access pattern. Tune each CF independently. Cheap to evolve over time.

Secondary indexes

Maintain a CF where keys are the index entry and values point to the primary key. Update both atomically with a WriteBatch.

Cheap purge

Need to wipe a tenant's data? Put each tenant in its own CF and DropColumnFamily to nuke it.

© 2026 RocksDB-Sharp. All rights reserved.