Overview
leveldb wraps Google's LevelDB — the original LSM-tree key-value store that later inspired RocksDB. It stores key-value pairs on disk, keeps keys in sorted order, and is optimized for write-heavy workloads. No SQL, no server, no configuration file — just open a path and start reading and writing.
LevelDB compresses data with Snappy by default, supports consistent read snapshots, and can apply many operations atomically through write batches. It is a foundational building block for databases, caches, and append-heavy storage systems.
Requires libleveldb-dev. Ubuntu/Debian: sudo apt install libleveldb-dev
Key Characteristics
| Feature | Details |
|---|---|
| LSM-tree | Log-Structured Merge-tree — writes go to a memory buffer and are flushed to disk in sorted runs. Optimized for write throughput over random reads. |
| Sorted keys | Iterator always traverses keys in lexicographic order. Prefix scans and range queries are efficient. |
| Snapshots | Obtain a consistent point-in-time view of the database. Reads against a snapshot are isolated from concurrent writes. |
| Write batches | Group multiple puts and deletes into one atomic operation. Either all changes appear or none do. |
| Snappy compression | Enabled by default. Reduces disk usage significantly for text-like values with minimal CPU overhead. |
| Bloom filter | Optional per-key bloom filter. Set BloomFilterBits = 10 to dramatically reduce disk reads for keys that do not exist. |
| Single-process limit | Only one process may open a database at a time. LevelDB acquires an exclusive file lock on open. |
Comparison
| Library | Compared to LevelDB |
|---|---|
| RocksDB | Meta-maintained fork with column families, transactions, more tuning options. LevelDB is simpler, smaller codebase, sufficient for most use cases. |
| LMDB | Memory-mapped B-tree. Much faster for random reads and concurrent readers. LevelDB wins for write throughput and smaller RAM footprint. |
| SQLite | Full SQL engine with transactions and indexes. LevelDB has no SQL, but is significantly faster for raw key-value operations and streaming writes. |
Key size: recommended < 1 KB. Value size: recommended < 1 MB (larger values work but degrade compaction performance). Write batch: recommended < 4 MB per flush.
Quick Start
Open, write, read, close
uses leveldb;
{ Open (or create) a database at the given path }
var db := Open('/var/data/mydb');
Put(db, 'user:1001:name', 'Alice');
Put(db, 'user:1001:email', 'alice@example.com');
var name := Get(db, 'user:1001:name');
WriteLn('Name: ' + name);
Delete(db, 'user:1001:email');
Close(db);
Write batch (atomic multi-key update)
uses leveldb;
var db := Open('/var/data/mydb');
var b := NewBatch;
BatchPut(b, 'account:42:balance', '1500');
BatchPut(b, 'account:42:updated', '2026-03-13');
BatchDelete(b, 'account:42:pending');
WriteBatch(db, b); { all three ops are applied atomically }
FreeBatch(b);
Close(db);
Prefix-range iteration with snapshot
uses leveldb;
var db := Open('/var/data/mydb');
var snap := NewSnapshot(db);
var ropts: TLevelReadOptions;
ropts.VerifyChecksums := False;
ropts.FillCache := False;
ropts.Snapshot := snap; { read against frozen state }
var it := NewIteratorEx(db, ropts);
IterSeek(it, 'user:'); { jump to first key >= 'user:' }
while IterValid(it) do
begin
var k := IterKey(it);
if Copy(k, 1, 5) <> 'user:' then Break;
WriteLn(k + ' = ' + IterValue(it));
IterNext(it);
end;
FreeIterator(it);
FreeSnapshot(db, snap);
Close(db);
Types
type
TLevelDB = Integer; { database handle returned by Open/OpenEx }
TLevelIter = Integer; { iterator handle returned by NewIterator/NewIteratorEx }
TLevelSnap = Integer; { snapshot handle returned by NewSnapshot }
TLevelBatch = Integer; { write batch handle returned by NewBatch }
TLevelOptions = record
CreateIfMissing : Boolean; { create DB if it doesn't exist }
ErrorIfExists : Boolean; { fail if DB already exists }
ParanoidChecks : Boolean; { verify checksums on every read }
WriteBufferSize : Integer; { bytes, default 4 MB }
MaxOpenFiles : Integer; { default 1000 }
BlockSize : Integer; { bytes per block, default 4 KB }
BlockCacheSize : Integer; { bytes, 0 = no LRU block cache }
BloomFilterBits : Integer; { bits per key, 0 = disabled, 10 recommended }
CompressionSnappy : Boolean; { enable Snappy compression, default True }
end;
TLevelWriteOptions = record
Sync : Boolean; { fsync after write — slower but durable across crashes }
end;
TLevelReadOptions = record
VerifyChecksums : Boolean; { check block checksums on reads }
FillCache : Boolean; { populate block cache from reads }
Snapshot : TLevelSnap; { 0 = read current state }
end;
Open / Close API
Open creates or opens a LevelDB database at the given filesystem path. Only one process may hold the database open at a time.
| Function | Description |
|---|---|
| Open(Path) | Open (or create) a database at Path. Uses default options: CreateIfMissing=True, Snappy on. |
| OpenEx(Path, Opts) | Open with a TLevelOptions record for full control over cache, bloom filter, write buffer, etc. |
| Close(DB) | Flush pending writes and release the file lock. Always call before the process exits. |
| Destroy(Path) | Delete all database files at Path. The database must not be open. |
| Repair(Path) | Attempt to recover a corrupted database. May lose recently written data. |
Basic Read / Write API
All keys and values are strings. Binary data is supported — use any encoding you like; LevelDB treats keys and values as opaque byte sequences.
| Function | Description |
|---|---|
| Put(DB, Key, Value) | Write a key-value pair. Overwrites any existing value for that key. |
| PutEx(DB, Key, Value, Opts) | Write with TLevelWriteOptions. Set Sync=True for durable writes that survive power loss. |
| Get(DB, Key) | Read a value by key. Returns '' (empty string) if the key does not exist. |
| GetEx(DB, Key, Opts) | Read with TLevelReadOptions. Pass a snapshot to read a frozen view of the database. |
| Exists(DB, Key) | Returns True if the key exists. Faster than Get when you only need presence. |
| Delete(DB, Key) | Remove a key. Does nothing if the key does not exist. |
Write Batch API
A write batch groups multiple puts and deletes into one atomic operation applied in a single disk write. This is both safer (all-or-nothing) and faster (one fsync) than issuing individual writes.
| Function | Description |
|---|---|
| NewBatch | Allocate a new empty write batch. Returns a TLevelBatch handle. |
| FreeBatch(B) | Release the batch and its memory. Call after WriteBatch. |
| BatchPut(B, Key, Value) | Stage a put operation in the batch. |
| BatchDelete(B, Key) | Stage a delete operation in the batch. |
| BatchClear(B) | Discard all staged operations without freeing the batch handle (allows reuse). |
| WriteBatch(DB, B) | Apply all staged operations atomically. |
| WriteBatchEx(DB, B, Opts) | Apply with TLevelWriteOptions (e.g. Sync=True). |
Iterator API
Iterators traverse keys in sorted lexicographic order. Because LevelDB stores keys sorted, a seek to a prefix followed by sequential IterNext calls is the canonical way to scan a range.
| Function | Description |
|---|---|
| NewIterator(DB) | Create an iterator over the current database state. |
| NewIteratorEx(DB, Opts) | Create an iterator with read options. Pass a snapshot to freeze the view. |
| FreeIterator(It) | Release the iterator. Must be called before FreeSnapshot if a snapshot was used. |
| IterSeekFirst(It) | Move to the first (smallest) key in the database. |
| IterSeekLast(It) | Move to the last (largest) key in the database. |
| IterSeek(It, Target) | Move to the first key ≥ Target. Use for prefix scans. |
| IterNext(It) | Advance to the next key in sorted order. |
| IterPrev(It) | Move to the previous key (reverse scan). |
| IterValid(It) | Returns True when the iterator points to a valid key. Check before calling IterKey/IterValue. |
| IterKey(It) | Return the key at the current iterator position. |
| IterValue(It) | Return the value at the current iterator position. |
Snapshots API
A snapshot captures the state of the database at the moment it is created. All reads performed against a snapshot return consistent results regardless of concurrent writes, making snapshots ideal for backups and long-running scans.
| Function | Description |
|---|---|
| NewSnapshot(DB) | Create a consistent snapshot of the current database state. Returns a TLevelSnap handle. |
| FreeSnapshot(DB, Snap) | Release the snapshot. LevelDB cannot compact data covered by live snapshots, so always free them when done. |
Create the snapshot first, then create an iterator with NewIteratorEx passing the snapshot in TLevelReadOptions.Snapshot. Free the iterator before freeing the snapshot.
Maintenance API
These functions expose LevelDB's internal maintenance and diagnostic operations.
| Function | Description |
|---|---|
| CompactRange(DB, StartKey, EndKey) | Manually trigger compaction of the given key range. Pass '' for both arguments to compact the entire database. Useful after bulk deletes to reclaim disk space. |
| GetProperty(DB, Property) | Read an internal diagnostic property. Useful values: 'leveldb.stats' (compaction stats), 'leveldb.num-files-at-level0', 'leveldb.approximate-memory-usage'. |
| ApproximateSize(DB, StartKey, EndKey) | Return the approximate number of bytes of disk space used by keys in the range [StartKey, EndKey]. |
| LevelDBVersion | Return the LevelDB library version string (e.g. '1.23'). |
uses leveldb;
var db := Open('/var/data/mydb');
{ Print internal stats }
WriteLn(GetProperty(db, 'leveldb.stats'));
{ Compact entire DB after a bulk delete pass }
CompactRange(db, '', '');
var sz := ApproximateSize(db, 'user:', 'user;');
WriteLn('User prefix ~' + IntToStr(sz) + ' bytes on disk');
Close(db);
Key Concepts
LSM-tree write path
Writes land first in an in-memory buffer (the MemTable). When full, the MemTable is flushed to a sorted on-disk file (an SSTable) at level 0. Background compaction threads merge and re-sort SSTables across levels, reclaiming space from deleted or overwritten keys. This design makes sequential writes very fast at the cost of read amplification on older data.
Key design for prefix scans
Because LevelDB keeps keys sorted, you can model relational-style access with structured key prefixes such as entity:id:field. A seek to 'user:1001:' followed by sequential IterNext calls will visit all fields of that user in one pass, reading from a contiguous range of the sorted file.
Sync writes
By default, Put and WriteBatch write to the OS page cache and may be lost if the process crashes before the OS flushes to disk. Set TLevelWriteOptions.Sync = True to call fsync after each write. This is roughly 100× slower but guarantees durability across power failures.
Package Info
System Requirements
libleveldb-devUbuntu/Debian:
sudo apt install libleveldb-dev
TLevelOptions Fields
.CreateIfMissing — create if absent.ErrorIfExists — fail if already exists.ParanoidChecks — checksum all reads.WriteBufferSize — MemTable size (bytes).MaxOpenFiles — open SSTable limit.BlockSize — SSTable block size.BlockCacheSize — LRU cache (0=off).BloomFilterBits — 0=off, 10=recommended.CompressionSnappy — Snappy on/off
Functions
- Open / OpenEx
- Close
- Destroy / Repair
- Put / PutEx
- Get / GetEx
- Exists / Delete
- NewBatch / FreeBatch
- BatchPut / BatchDelete
- BatchClear
- WriteBatch / WriteBatchEx
- NewIterator / NewIteratorEx
- FreeIterator
- IterSeekFirst / IterSeekLast
- IterSeek
- IterNext / IterPrev
- IterValid
- IterKey / IterValue
- NewSnapshot / FreeSnapshot
- CompactRange
- GetProperty
- ApproximateSize
- LevelDBVersion