libmdbx
Встраиваемая база данных для надёжных систем

libmdbx

Быстрая и надёжная встраиваемая база данных формата key-value. Создана для высоконагруженных систем, где важны производительность, устойчивость к сбоям и предсказуемость работы — от встраиваемых устройств до космической телеметрии.

ACID-транзакции
Высокая производительность
Минимальные накладные расходы

Проверено сообществом

libmdbx используется в сотнях проектов с открытым исходным кодом и в инфраструктуре Ethereum (Erigon, Reth и другие), а также вошёл в число победителей конкурса Yandex Open Source среди открытых проектов.

Обзор библиотеки

Что такое libmdbx

libmdbx — исключительно быстрый, компактный и мощный встраиваемый транзакционный key-value движок без WAL. Он позволяет рою многопоточных процессов выполнять ACID-чтение и запись нескольких карт и мультикарт в локально разделяемой базе, обеспечивая экстраординарную производительность при минимальных накладных расходах за счёт отображения в память и операций O(log N) на B+ дереве. Не требует обслуживания и восстановления после сбоя, гарантирует целостность данных и свободно распространяется по лицензии Apache 2.0.

Полный ACID

Атомарные, согласованные, изолированные и долговечные транзакции на основе MVCC и copy-on-write. Целостность данных гарантируется даже после сбоя.

Без WAL и восстановления

Нет журнала упреждающей записи и восстановления после сбоя. Благодаря теневой подкачке страниц база не требует обслуживания и не растёт бесконтрольно.

Высокая производительность

Данные отображаются в память (memory-mapped) и читаются напрямую без копирования. Операции поиска, вставки, обновления и удаления — O(log N) благодаря B+ дереву.

Параллельный доступ

Рой многопоточных процессов выполняет ACID-чтение и запись в общую базу. Читатели не блокируются и не требуют атомарных операций, масштабируясь по ядрам.

Строго последовательные изменения

Изменения вносятся строго последовательно через единственный мьютекс — исключены конфликты транзакций и взаимоблокировки. Писатели не блокируют читателей и наоборот.

Несколько подбаз

Множество key-value таблиц и подбаз в одном файле данных, включая эффективные multimap: упорядоченные, искомые и обходимые множества значений без дублирования ключей.

Кроссплатформенность

Поддержка Linux, Windows, macOS, Android, iOS, FreeBSD, Solaris и других систем, совместимых с POSIX.1-2008.

Компактность и встраивание

Всего несколько плоских файлов исходного кода, никаких внутренних потоков и серверных процессов. Полностью пригодно для глубокого встраивания.

Возможности и особенности

  • Модель данных key-value, ключи всегда отсортированы
  • Полная поддержка ACID через MVCC и copy-on-write
  • Несколько таблиц и подбаз в одном файле данных
  • Диапазонные запросы, включая оценку объёма выборки
  • Эффективная работа с короткими ключами фиксированной длины, включая нативные 32/64-битные целые
  • Сверхэффективная поддержка multimap — отсортированные, искомые и обходимые множества значений без дублирования ключей
  • Данные отображаются в память и доступны напрямую, без копирования (zero-copy)
  • Транзакции чтения и записи не блокируют друг друга
  • Нет конфликтов транзакций и взаимоблокировок — изменения вносятся строго последовательно
  • Читатели не блокируются при сохранении snapshot-изоляции
  • Чтение масштабируется линейно по числу ядер CPU
  • Явная дефрагментация и непрерывная компактификация с нулевыми накладными расходами
  • Автоматическая подстройка размера базы на лету
  • Стоимость операций O(log N) благодаря B+ дереву
  • Очень быстрые get(key), ускоренные разделяемым lock-free кэшем
  • Онлайн-горячее резервное копирование
  • Операция append для быстрой пакетной вставки предварительно отсортированных данных
  • Нет WAL и журнала транзакций: не нужны восстановление и обслуживание
  • Гибкий API транзакций с поддержкой вложенности
  • Настраиваемый размер страницы базы

Доработки по сравнению с LMDB

libmdbx — глубоко переработанный и расширенный наследник легендарной Lightning Memory-Mapped Database. Он наследует все преимущества LMDB, но устраняет ряд проблем и добавляет большой набор доработок. В сравнении с LMDB libmdbx заставляет вещи «просто работать» идеально и из коробки, а не тихо и катастрофически ломаться.

  • Ключи могут быть более чем в 2 раза длиннее, чем в LMDB, с поддержкой ключей и значений нулевой длины
  • До 30% быстрее LMDB в CRUD-бенчмарках
  • Автоматическая подстройка размера базы на лету — и увеличение, и уменьшение
  • Непрерывная компактификация базы с нулевыми накладными расходами
  • Единый формат файла базы для 32- и 64-битных сборок
  • Возможность «Big Foot», решающая проблемы производительности с гигантскими транзакциями и сверхбольшими списками номеров страниц
  • Политика LIFO для переработки страниц сборщиком мусора — заметный рост скорости записи
  • Парковка read-транзакций с вытеснением и автоперезапуском и колбэк Handle-Slow-Readers
  • Быстрая оценка объёма результата диапазонного запроса
  • API проверки целостности базы и автономная утилита mdbx_chk
  • Открытие баз в эксклюзивном режиме, в том числе на сетевых шарах
  • Расширенная информация о базе целиком, таблицах/подбазах, транзакциях и списке читателей
  • Возможность «get-cached» с лёгким прозрачным кэшем
  • Клонирование read-транзакций и «воскрешение» транзакции после fork
  • Автоматическая регулярная синхронизация на диск по порогам и/или таймауту через дешёвый polling
  • Расширенные операции обновления и быстрого удаления
  • Возможность определить, находится ли конкретный элемент на грязной странице
  • Генерация последовательностей и три постоянных 64-битных маркера типа vector-clock
  • Полезные runtime-опции для тонкой настройки движка
Примеры и сниппеты

Как использовать libmdbx

Типовые сценарии работы с базой — от базовой записи и чтения до транзакций ACID и курсоров. Примеры даны на разных языках: от родного C и C++ API до биндингов Rust и Python.

C

Первый шаг: открыть, записать, прочитать

Базовый сценарий на C. Создаётся окружение базы, в write-транзакции открывается таблица и сохраняется пара ключ–значение, затем в read-only транзакции значение читается. Транзакция обязательна даже для чтения — она даёт согласованный снимок данных.

C
#include <mdbx.h>
#include <stdio.h>

int main(void) {
  MDBX_env *env;
  MDBX_txn *txn;
  MDBX_dbi dbi;

  mdbx_env_create(&env);
  /* MDBX_NOSUBDIR: путь — это сам файл данных */
  mdbx_env_open(env, "./mydb.mdb", MDBX_NOSUBDIR, 0664);

  /* Транзакция обязательна, даже для чтения */
  mdbx_txn_begin(env, NULL, MDBX_TXN_READWRITE, &txn);
  mdbx_dbi_open(txn, NULL, MDBX_CREATE, &dbi);

  MDBX_val key = { "answer", 6 };
  MDBX_val val = { "42", 2 };
  mdbx_put(txn, dbi, &key, &val, 0); /* 0 = UPSERT */

  mdbx_txn_commit(txn);

  MDBX_val out = { 0 };
  mdbx_txn_begin(env, NULL, MDBX_TXN_READONLY, &txn);
  mdbx_get(txn, dbi, &key, &out);
  printf("answer = %.*s\n", (int)out.iov_len, (char *)out.iov_base);
  mdbx_txn_abort(txn);

  mdbx_env_close(env);
  return 0;
}

C

Транзакции ACID: commit и abort

Все изменения внутри транзакции применяются атомарно. При ошибке транзакция откатывается целиком — ни одна из записей не попадает в базу. Именно так libmdbx гарантирует целостность данных.

C
/* Атомарная групповая запись */
mdbx_txn_begin(env, NULL, MDBX_TXN_READWRITE, &txn);

MDBX_val a = { "alpha", 5 }, va = { "1", 1 };
MDBX_val b = { "beta",  4 }, vb = { "2", 1 };
mdbx_put(txn, dbi, &a, &va, 0);
mdbx_put(txn, dbi, &b, &vb, 0);

if (something_failed) {
  mdbx_txn_abort(txn);  /* откат: ни одна запись не применится */
} else {
  mdbx_txn_commit(txn); /* фиксация: обе записи видны атомарно */
}

C

Курсоры и диапазонные обходы

Курсор позволяет перебирать ключи в отсортированном порядке и выбирать диапазоны. В отличие от LMDB, в libmdbx курсор всегда закрывается явно — это исключает утечки и ошибки повторного использования.

C
MDBX_cursor *cur;
mdbx_txn_begin(env, NULL, MDBX_TXN_READONLY, &txn);
mdbx_cursor_open(txn, dbi, &cur);

MDBX_val key, val;
/* MDBX_NEXT перебирает ключи в отсортированном порядке */
while (mdbx_cursor_get(cur, &key, &val, MDBX_NEXT) == MDBX_SUCCESS) {
  printf("%.*s = %.*s\n",
         (int)key.iov_len, (char *)key.iov_base,
         (int)val.iov_len,  (char *)val.iov_base);
}
/* В libmdbx курсор всегда закрывается явно */
mdbx_cursor_close(cur);
mdbx_txn_abort(txn);

C++

RAII-обёртка C++ API

C++ API прячет управление ресурсами: окружение и транзакции закрываются автоматически при выходе из области видимости. Ключи и значения передаются типизированными строками вместо сырых указателей.

C++
#include <mdbx.h++>

// Окружение управляется автоматически (RAII)
mdbx::env_managed env =
    mdbx::env_managed::create("mydb.mdb",
        mdbx::env_managed::operate_parameters{});

auto txn = env.start_write();
auto db  = txn.open_map("records");

txn.put(db, "telemetry", "ok"); /* типизированные строки */
txn.commit();

/* env закроется сам при выходе из области видимости */

Rust

Типобезопасный доступ из Rust

Биндинг libmdbx-rs даёт безопасный API: ошибки обрабатываются через Result, а ключи и значения — через байтовые срезы. Таблица создаётся при первом открытии в write-транзакции.

Rust
use libmdbx::{Environment, WriteFlags};

let env = Environment::new("mydb.mdb")?;

/* таблица создаётся при первом открытии в write-транзакции */
let txn = env.begin_rw_txn()?;
let db  = txn.open_db(None)?;
txn.put(db, b"answer", b"42", WriteFlags::UPSERT)?;
txn.commit()?;

let txn = env.begin_ro_txn()?;
let val = txn.get(db, b"answer")?;
assert_eq!(val, Some(&b"42"[..]));

Python

Key-value за пару строк

Python-биндинг делает работу с libmdbx краткой: открытая среда, транзакция и пара ключ–значение. Контекстный менеджер сам фиксирует транзакцию и закрывает ресурсы.

Python
import libmdbx

env = libmdbx.Environment("mydb.mdb", max_dbs=1)
db  = env.open_db()

txn = env.begin(write=True)
txn.put(b"answer", b"42", db=db)
txn.commit()

with env.begin() as txn:
    print(txn.get(b"answer", db=db))

Канонический интерфейс libmdbx — это C и C++ API. Биндинги на других языках разрабатываются сторонними авторами, поэтому их сигнатуры могут отличаться между версиями — сверяйтесь с документацией конкретного пакета.

Документация и справка

Справочник libmdbx

Параметры окружения, флаги открытия и ключевые функции C API. Структурированная справка для быстрого старта и тонкой настройки базы.

Параметры окружения

Настройки, передаваемые через структуру MDBX_options при создании окружения.

ПараметрТипНазначение
size_lower / size_upperuint64Нижний и верхний пределы размера файла базы. Без них база растёт без ограничений и может съесть весь диск.
size_nowuint64Начальный размер файла данных при первом создании. Задаётся заранее, чтобы избежать частого расширения.
grow_stepuint64Шаг, на который файл автоматически увеличивается по мере роста данных. По умолчанию — разумная доля текущего размера.
shrink_thresholduint64Порог автоматического уменьшения файла. Позволяет базу компактно сжимать, когда данные удаляются.
max_readersuint32Максимальное число одновременных читателей (слотов в LCK-файле). Защищает от исчерпания памяти.
max_dbsuint32Максимальное число именованных таблиц (подбаз) в окружении. По умолчанию достаточно для большинства задач.
max_mapsizeuint64Верхняя граница размера memory-mapped области. Управляет объёмом виртуальной памяти окружения.
sync_bytesint64Порог автосинхронизации: объём «грязных» данных, после которого запускается сброс на диск.
sync_perioddoubleПериод автосинхронизации в секундах. Позволяет настраивать баланс между долговечностью и скоростью записи.
Больше информации

Флаги открытия окружения

Комбинируются побитово и передаются в mdbx_env_open().

  • MDBX_NOSUBDIR

    Путь — это сам файл данных, без отдельной папки и lock-файла с суффиксом.

  • MDBX_RDONLY

    Открывает базу только для чтения. Писатели блокируются.

  • MDBX_WRITEMAP

    Прямая запись в memory-mapped область без копирования страниц.

  • MDBX_NOMETASYNC

    Не сбрасывать метаданные при каждой синхронизации, повышая скорость.

  • MDBX_SAFE_NOSYNC

    Ослабленная синхронизация для высоконагруженных рабочих нагрузок.

  • MDBX_UTTERLY_NOSYNC

    Вообще не синхронизировать данные — максимальная скорость, риск потери.

  • MDBX_NOTLS

    Не использовать thread-local storage для читателей.

  • MDBX_NORDAHEAD

    Отключает упреждающее чтение при обходах, экономя память.

Больше информации

Функции C API

Основные функции, сгруппированные по назначению. Полный список — в официальной документации C API.

Жизненный цикл

  • mdbx_env_create

    Создаёт дескриптор окружения базы

  • mdbx_env_open

    Открывает базу данных по пути

  • mdbx_env_close

    Закрывает окружение и освобождает ресурсы

  • mdbx_env_delete

    Удаляет файлы базы и блокировки

Транзакции

  • mdbx_txn_begin

    Начинает read-write или read-only транзакцию

  • mdbx_txn_commit

    Атомарно фиксирует изменения

  • mdbx_txn_abort

    Откатывает все изменения транзакции

  • mdbx_txn_reset

    Сбрасывает состояние read-only транзакции

Таблицы (подбазы)

  • mdbx_dbi_open

    Открывает или создаёт именованную таблицу

  • mdbx_dbi_close

    Закрывает дескриптор таблицы

  • mdbx_dbi_stat

    Возвращает статистику по таблице

  • mdbx_dbi_flags

    Возвращает флаги и настройки таблицы

CRUD-операции

  • mdbx_put

    Вставляет или обновляет ключ–значение

  • mdbx_get

    Читает значение по ключу

  • mdbx_del

    Удаляет запись по ключу

  • mdbx_canary_put

    Записывает маркеры окружения

Курсоры

  • mdbx_cursor_open

    Открывает курсор для обхода таблицы

  • mdbx_cursor_get

    Ищет и перебирает записи

  • mdbx_cursor_put

    Записывает запись через курсор

  • mdbx_cursor_close

    Закрывает курсор

Настройка

  • mdbx_env_set_geometry

    Задаёт геометрию роста файла базы

  • mdbx_env_set_maxdbs

    Устанавливает число именованных таблиц

  • mdbx_env_set_syncbytes

    Настраивает порог автосинхронизации

  • mdbx_env_set_syncperiod

    Настраивает период автосинхронизации

Больше информации

C++ API libmdbx

C++ API — это основной нативный интерфейс libmdbx наравне с C API. Он построен поверх C API и добавляет строгую типизацию, RAII-управление ресурсами и исключения вместо кодов ошибок. Срезы (slice), управляемые транзакции и курсоры делают код короче и безопаснее: окружение, транзакции и курсоры закрываются автоматически, а ошибки превращаются в исключения типа mdbx::error. Подключается одним заголовочным файлом mdbx.h++.

Ключевые возможности C++ API

Полный RAII

env_managed, txn_managed и cursor_managed автоматически закрывают окружение, завершают транзакции (commit или abort) и освобождают курсоры при выходе из области видимости. Утечки ресурсов практически исключены.

Типобезопасные срезы

mdbx::slice и mdbx::buffer заменяют сырые пары указатель-длина (MDBX_val). Ключи и значения передаются как типизированные строки и двоичные срезы без ручного подсчёта длины.

Исключения вместо кодов

Ошибки возбуждаются как исключения mdbx::error с понятным сообщением. Это позволяет писать чистый линейный код без повсеместной проверки возвращаемых кодов.

Режимы ключей и значений

Декларативные перечисления key_mode и value_mode описывают сортировку ключей (usual, reverse, ordinal) и наличие мультизначений (single, multi). Никаких «магических» битовых флагов.

Гибкие аллокаторы

Современные API работают с polymorphic_allocator и стандартными memory resources (std::pmr). Буферы можно создавать прямо из slice в контексте транзакции.

Тонкая настройка окружения

Параметры передаются через структуру operate_parameters, а рост файла базы описывается объектом geometry с методами make_dynamic() и make_fixed() — всё конфигурируется типизированно и проверяется на этапе компиляции.

Основные классы

Ключевые типы пространства имён mdbx. Управляемые версии (suffix _managed) берут владение ресурсами на себя и закрывают их автоматически.

mdbx::env

Неуправляемое окружение базы — низкоуровневая обёртка над MDBX_env без владения ресурсами.

mdbx::env_managed

Управляемое окружение с RAII. Создаётся конструктором (открывает или создаёт базу) и автоматически закрывается при выходе из области видимости.

mdbx::txn

Неуправляемая транзакция. Позволяет вручную управлять жизненным циклом через commit() и abort().

mdbx::txn_managed

Управляемая транзакция с RAII. Получается через env.start_write() / env.start_read(); на деструкторе сама фиксирует или откатывает изменения.

mdbx::cursor / cursor_managed

Курсоры для поиска и обхода записей в отсортированном порядке. cursor_managed закрывается автоматически, как того требует libmdbx.

mdbx::map_handle

Дескриптор таблицы/подбазы в окружении. Открывается через txn.open_map() с указанием режимов ключей и значений.

mdbx::slice

Невладеющий срез данных (ключ или значение): пара указатель-длина с богатым набором методов (as_string, hex/base64, head, tail, сравнение).

mdbx::buffer

Владеющий буфер данных, который сам управляет памятью. Удобен для копирования значений, хранящихся в транзакции, в долгоживущие объекты.

mdbx::error

Класс ошибки/исключения. Позволяет отличать успех, «нет данных» и реальные сбои, а также получать код и сообщение ошибки.

Развёрнутые примеры

Полные сценарии на C++ API: от создания окружения и базового CRUD до мультизначений, тонкой настройки и обработки ошибок. Все примеры используют RAII и не требуют ручного освобождения ресурсов.

Окружение и базовый CRUD

Окружение создаётся конструктором env_managed и закрывается само. В write-транзакции открывается таблица и сохраняются пары ключ–значение, затем в read-only транзакции значение читается. Перечисление put_mode выбирает режим записи: upsert перезаписывает значение, если ключ уже есть.

C++
#include <mdbx.h++>
#include <iostream>
#include <string>

int main() {
  // RAII: окружение закроется само при выходе из области видимости
  mdbx::env_managed env("mydb.mdb",
      mdbx::env_managed::operate_parameters{});

  // write-транзакция фиксируется при выходе из блока
  {
    auto txn = env.start_write();
    auto db  = txn.open_map("records");
    txn.put(db, "answer",  "42",             mdbx::put_mode::upsert);
    txn.put(db, "greeting", "hello, cosmos", mdbx::put_mode::upsert);
    txn.commit();
  }

  // read-only транзакция даёт согласованный снимок данных
  auto txn  = env.start_read();
  auto db   = txn.open_map("records");
  auto data = txn.get(db, "answer");
  std::cout << "answer = "
            << std::string(data.char_ptr(), data.length()) << "\n";
}
Транзакции ACID и откат

Все изменения применяются атомарно. txn_managed на деструкторе сам вызывает commit() при нормальном завершении и abort() при выходе по исключению. Явный вызов abort() откатывает изменения целиком — ни одна запись не попадёт в базу.

C++
#include <mdbx.h++>

void apply_batch(mdbx::env_managed& env) {
  auto txn = env.start_write();
  auto db  = txn.open_map("records");

  txn.put(db, "alpha", "1", mdbx::put_mode::upsert);
  txn.put(db, "beta",  "2", mdbx::put_mode::upsert);

  if (something_failed) {
    txn.abort();  // откат: обе записи не применится
  } else {
    txn.commit(); // фиксация: обе записи видны атомарно
  }
}
Обход с курсором

Курсор перебирает записи в отсортированном порядке. cursor_managed закрывается автоматически — явно освобождать его не нужно, в отличие от C API. Методы key() и value() возвращают срез с ключом и значением текущей записи.

C++
#include <mdbx.h++>
#include <iostream>
#include <string>

auto txn    = env.start_read();
auto db     = txn.open_map("records");
auto cursor = txn.open_cursor(db); // RAII, закроется сам

for (cursor.to_first(); !cursor.eof(); cursor.to_next()) {
  auto key   = cursor.key();
  auto value = cursor.value();
  std::cout << std::string(key.char_ptr(), key.length())
            << " = "
            << std::string(value.char_ptr(), value.length()) << "\n";
}
Мультизначения (value_mode::multi)

Таблица, созданная с режимом value_mode::multi (аналог MDBX_DUPSORT), хранит для одного ключа упорядоченный набор значений. Вставка insert_unique добавляет новое значение, не заменяя существующие. Чтение возвращает все значения ключа и их количество.

C++
#include <mdbx.h++>
#include <iostream>

{
  auto txn = env.start_write();
  // одному ключу соответствует упорядоченный набор значений
  auto db = txn.open_map("tags",
      mdbx::key_mode::usual, mdbx::value_mode::multi);

  txn.put(db, "fruit", "apple",  mdbx::put_mode::upsert);
  txn.put(db, "fruit", "banana", mdbx::put_mode::upsert);
  txn.put(db, "fruit", "cherry", mdbx::put_mode::upsert);
  txn.commit();
}

auto txn = env.start_read();
auto db  = txn.open_map("tags",
    mdbx::key_mode::usual, mdbx::value_mode::multi);

size_t count = 0;
auto first = txn.get(db, "fruit", count); // все значения ключа
std::cout << "fruit: " << count << " values, first = "
          << std::string(first.char_ptr(), first.length()) << "\n";
Тонкая настройка геометрии

Параметры окружения передаются через структуру operate_parameters. max_readers ограничивает число читателей, max_maps — число именованных таблиц, а geometry задаёт динамический рост файла базы между нижним и верхним пределами.

C++
#include <mdbx.h++>

mdbx::env_managed::operate_parameters params;
params.max_readers = 64;   // слотов для читателей
params.max_maps    = 32;   // именованных таблиц
// база автоматически растёт от 1 MiB до 1 TiB
params.geometry.make_dynamic(1ull << 20, 1ull << 40);

mdbx::env_managed env("telemetry.mdb", params);

auto txn = env.start_write();
auto db  = txn.open_map("sensors");
txn.put(db, "node-0", "online", mdbx::put_mode::upsert);
txn.commit();
Обработка ошибок и кодирование

C++ API возбуждает исключения mdbx::error. Попытка вставить существующий ключ с insert_unique завершится исключением, которое легко перехватить. Срезы умеют представлять данные в hex/base64/base58-формате для отладки и логирования.

C++
#include <mdbx.h++>
#include <iostream>

try {
  auto txn = env.start_write();
  auto db  = txn.open_map("records");

  // ключ уже существует -> insert_unique возбудит исключение
  txn.put(db, "answer", "42", mdbx::put_mode::insert_unique);

  auto value = txn.get(db, "answer");
  std::cout << "answer hex = " << value.as_hex_string() << "\n";
  txn.commit();
} catch (const mdbx::error& e) {
  std::cerr << "mdbx error: " << e.what() << "\n";
}
Больше информации
Ссылки и ресурсы

Ресурсы libmdbx

Быстрые ссылки на исходный код, загрузку и внешние ресурсы библиотеки. Скачивайте релизы, изучайте документацию и подключайтесь к сообществу.

Репозиторий

Исходный код библиотеки и релизы на основной площадке и в зеркале.

Загрузка

Стабильные релизы, архивы исходников и история изменений для загрузки.

Документация

Официальное руководство и справочники C/C++ API на сайте проекта.

Сообщество и связь

Каналы обратной связи: вопросы, предложения и улучшения библиотеки.

Вопросы и ответы

Вопросы и ответы

Типичные вопросы и ответы на основе реальной переписки сообщества в Telegram, обсуждений Q&A и закрытых issues. Ответы отражают практический опыт использования libmdbx.

Общие вопросы

Производительность и настройка

Транзакции и курсоры

Целостность и восстановление

Платформы и сборка

Биндинги и экосистема