Skip to content

DecentDB vs LMDB: When to Choose Which

This document helps developers decide between DecentDB and LMDB (Lightning Memory-Mapped Database) for embedded storage workloads. Both use B-tree variants and prioritize read performance, but they operate at different abstraction levels.

Versions compared: DecentDB 2.0.0 vs LMDB 0.9.33 (as of 2024).

See also: SQL Feature Matrix for DecentDB's full SQL surface, and DecentDB vs SQLite for the comparison with SQLite.

They Operate at Different Layers

  • LMDB is a key-value store with an ordered-map interface. It stores arbitrary byte-string keys and values. It has no concept of tables, columns, SQL, schemas, or queries beyond key lookup and range scans.
  • DecentDB is a relational database. It stores typed rows in tables with columns, supports SQL queries, indexes, joins, constraints, and transactions.

If you want DecentDB's feature set on top of LMDB, you would need to build the SQL layer, query planner, type system, and constraint enforcement yourself.

At a Glance

Dimension DecentDB LMDB
Abstraction level Relational database (SQL) Key-value store
Data model Tables, rows, columns, types Arbitrary key-value byte pairs
Query language SQL None (get/put/del/range API)
Indexing B-tree secondary indexes, trigram, expression, covering Single sorted key-space per database; multiple named databases
Durability WAL + fsync-on-commit, always Copy-on-write with configurable sync
Architecture B-tree B+tree with copy-on-write
Memory model Page cache (configurable) Memory-mapped (OS-managed)
Concurrency One writer, many concurrent reader threads (single process) One writer, many readers (multi-process safe)
Transactions Full ACID (SQL-level) ACID with MVCC
Background work None (B-tree manages space in-place) None (no compaction, no logs)
Bindings C ABI, Rust, Python, .NET, Go, Java, Node.js, Dart C, C++, Python, Rust, Go, Java, Node.js, Ruby, and many others
License MIT or Apache-2.0 OpenLDAP Public License (permissive)
Binary size ~2-3 MB ~64 KB
Platform support Tier 1 Rust platforms Unix, Linux, Windows, macOS, mobile
Implementation language Rust C

Architectural Similarities

Both databases share important design principles:

B-tree Family

  • DecentDB uses a B-tree for table storage and indexes
  • LMDB uses a B+tree with copy-on-write semantics

Both provide O(log n) lookups and efficient range scans. This is fundamentally different from LSM-tree databases like RocksDB or LevelDB.

Read-Optimized Design

Both prioritize read performance:

  • DecentDB: B-tree pages cached in memory, direct index seeks
  • LMDB: Memory-mapped files enable zero-copy reads directly from OS page cache

No Background Compaction

Unlike LSM-tree databases, neither requires background compaction:

  • DecentDB: In-place updates, space managed within B-tree pages
  • LMDB: Copy-on-write creates new pages, old pages remain valid for active readers

Key Differences

Memory Management

LMDB uses memory-mapped files: - OS manages the page cache automatically - Zero-copy reads: API returns pointers directly into mapped memory - Database size limited by virtual address space (128 TB on 64-bit systems) - No application-level cache tuning needed

DecentDB uses an explicit page cache: - Configurable cache size - Application controls memory usage - Works well on 32-bit systems without address space limits - Requires tuning for optimal performance

Cross-Process Concurrency

LMDB supports multi-process access: - Multiple processes can open the same database - Readers and writers from different processes coordinate via shared memory - MVCC ensures readers see consistent snapshots

DecentDB is single-process: - One process owns the database file - Multiple threads within that process can read concurrently - Simpler model, no cross-process coordination overhead

File Format Portability

LMDB files are architecture-dependent: - Not portable between 32-bit and 64-bit systems - Not portable between different endianness - Must export/import when moving between architectures

DecentDB uses a portable file format: - Same file works across supported platforms - Architecture-independent layout

Transaction Model

LMDB uses MVCC with copy-on-write: - Readers never block writers - Writers never block readers - Each read transaction sees a consistent snapshot - Write transactions are serialized (one at a time) - No transaction log needed (copy-on-write provides durability)

DecentDB uses WAL-based transactions: - Readers and writers can run concurrently - Write-ahead log ensures durability - Checkpointing keeps WAL bounded - Full SQL transaction semantics (BEGIN/COMMIT/ROLLBACK)

When LMDB Is the Better Fit

1. You need a key-value store, not SQL

If your data model is naturally key-value and you don't need queries, joins, or constraints, LMDB's simpler API is an advantage.

// LMDB: Simple key-value operations
MDB_val key, value;
key.mv_data = "user:123";
key.mv_size = 8;
value.mv_data = user_data;
value.mv_size = user_data_len;
mdb_put(txn, dbi, &key, &value, 0);

2. You need zero-copy reads

LMDB returns pointers directly into memory-mapped files. For large values, this avoids copying data into application buffers.

// LMDB: Zero-copy read
MDB_val key, value;
mdb_get(txn, dbi, &key, &value);
// value.mv_data points directly into the memory-mapped file
process_data(value.mv_data, value.mv_size);  // No copy needed

3. You need multi-process access

If multiple independent processes need to access the same database concurrently, LMDB's cross-process concurrency model is designed for this.

4. You want minimal code size

At ~64 KB of object code, LMDB is one of the smallest embedded databases available. If binary size is a critical constraint, LMDB is hard to beat.

5. You want OS-managed caching

LMDB relies on the OS page cache. There's no cache tuning, no buffer pool management, and no application-level memory accounting.

When DecentDB Is the Better Fit

1. You need SQL queries

-- DecentDB: Complex query in one statement
SELECT u.name, COUNT(o.id) as order_count, SUM(o.total) as total_spent
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.region = 'West'
  AND o.created_at > '2024-01-01'
GROUP BY u.id, u.name
HAVING COUNT(o.id) > 5
ORDER BY total_spent DESC
LIMIT 10;

With LMDB, you would need to: - Design key encoding for users and orders - Build and maintain secondary indexes manually - Implement the join logic in application code - Handle all the edge cases yourself

2. You need typed data and constraints

-- DecentDB: Schema with constraints
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    email TEXT NOT NULL UNIQUE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    CHECK (email LIKE '%@%')
);

CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    user_id INTEGER NOT NULL REFERENCES users(id),
    total DECIMAL(10,2) NOT NULL CHECK (total >= 0)
);

LMDB stores raw bytes. Type checking, constraints, and referential integrity are your responsibility.

3. You need secondary indexes

-- DecentDB: Automatic index management
CREATE INDEX idx_orders_user_id ON orders(user_id);
CREATE INDEX idx_orders_created ON orders(created_at);

With LMDB, you would maintain separate databases for each index and keep them consistent manually.

4. You need a portable file format

If your database files might be moved between different architectures (e.g., x86_64 to ARM), DecentDB's portable format avoids export/import steps.

5. You want a single-process, multi-threaded model

If your application is a single process with multiple threads, DecentDB's simpler concurrency model avoids the complexity of cross-process coordination.

Performance Characteristics

Workload DecentDB LMDB
Point lookup by key ~microseconds ~microseconds (zero-copy)
Range scan Efficient (B-tree) Efficient (B+tree)
Write throughput Good (WAL + in-place) Good (copy-on-write)
Read latency Predictable Predictable (OS-managed)
Memory control Explicit (cache size) Implicit (OS page cache)
Large value reads Copy required Zero-copy possible

Decision Matrix

Your Requirement Choose
SQL queries, joins, aggregations DecentDB
Key-value with range scans LMDB
Multi-process access LMDB
Single-process, multi-threaded Either (DecentDB for SQL)
Zero-copy large value reads LMDB
Typed data with constraints DecentDB
Secondary indexes DecentDB (automatic)
Minimal binary size LMDB
Portable file format DecentDB
OS-managed caching LMDB

Summary

  • Choose LMDB when you need a fast, compact key-value store with multi-process support and zero-copy reads.
  • Choose DecentDB when you need SQL queries, typed data, constraints, and automatic index management.

Both are excellent choices for embedded storage. The question is whether you want to build on a key-value foundation (LMDB) or start with a complete relational database (DecentDB).