Skip to content

LMDB

Databases & CachingDatabaseC++

What it is

LMDB (Lightning Memory-Mapped Database) is a high-performance, memory-mapped key-value store developed by the OpenLDAP project. It is compact, transactional, and highly efficient, designed for read-heavy workloads with low latency.

LMDB provides a transactional key-value store API, supporting ACID properties, multiple named databases, and concurrent readers. It is optimized for read-heavy workloads with very fast lookup performance.

Installation

sudo apt install liblmdb-dev

Getting started

The smallest useful thing you can do with it, and what each part means.

Creating and writing to a database
#include <lmdb.h>
#include <iostream>

int main() {
    MDB_env* env;
    MDB_dbi dbi;
    MDB_val key, data;
    MDB_txn* txn;
    MDB_cursor* cursor;

    // Create environment
    mdb_env_create(&env);
    mdb_env_set_maxdbs(env, 1);
    mdb_env_open(env, "./testdb", 0, 0664);

    // Start transaction
    mdb_txn_begin(env, NULL, 0, &txn);
    mdb_open(txn, NULL, 0, &dbi);

    key.mv_size = 5; key.mv_data = (void*)"hello";
    data.mv_size = 5; data.mv_data = (void*)"world";

    mdb_put(txn, dbi, &key, &data, 0);
    mdb_txn_commit(txn);

    mdb_close(env, dbi);
    mdb_env_close(env);
    return 0;
}
Creates an LMDB environment, inserts a key-value pair (`hello -> world`), and commits the transaction.

Advanced usage

Where the library earns its place over a simpler alternative.

Reading values
mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
mdb_cursor_open(txn, dbi, &cursor);
while (mdb_cursor_get(cursor, &key, &data, MDB_NEXT) == 0) {
    std::cout << (char*)key.mv_data << " => " << (char*)data.mv_data << std::endl;
}
mdb_cursor_close(cursor);
mdb_txn_abort(txn);
Iterates over all key-value pairs in the LMDB database.
Using multiple databases
mdb_open(txn, "users", MDB_CREATE, &dbi);
Opens a named sub-database inside the LMDB environment.
Transactions
mdb_txn_begin(env, NULL, 0, &txn);
// Perform operations
mdb_txn_commit(txn);
Ensures ACID compliance by explicitly wrapping operations in transactions.
Concurrent readers
mdb_txn_begin(env, NULL, MDB_RDONLY, &txn);
LMDB allows multiple concurrent read-only transactions without blocking writers.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

MDB_MAP_FULL
Occurs when the database map size is exceeded. Increase map size with `mdb_env_set_mapsize`.
MDB_BAD_TXN
Happens when using a transaction after it has been committed or aborted.
MDB_CORRUPTED
Indicates possible data corruption. Restore from backup if repair is not possible.

Best practices

  • Use LMDB for workloads with frequent reads and fewer writes.
  • Avoid long-running read transactions as they may block database growth.
  • Store small to medium-sized values; LMDB is not optimized for huge blobs.
  • Use named databases when organizing multiple datasets in the same environment.
  • Carefully size the LMDB map to avoid resizing overhead.

Background

Why it exists, and what it was reacting to.

LMDB was created by Howard Chu of the OpenLDAP project to provide a lightweight, transactional database for LDAP storage. Unlike traditional databases, LMDB uses memory-mapped files, allowing it to achieve excellent performance with minimal overhead. It has since been adopted widely in blockchain systems, distributed storage, and embedded applications.