Skip to content

Первые шаги

Смежные: Установка и сборка · Тулинг · Ограничения · Учебник: Том I «Основы» · Справочник C API

Всё начинается с окружения (MDBX_env): mdbx_env_create() → mdbx_env_open() → работа → mdbx_env_close(). Ненулевой аргумент mode в mdbx_env_open() разрешает создание БД и каталога, если их нет, и задаёт биты прав для новых файлов.

В каталоге создаются файл блокировок (LCK) и файл данных (DXB). Если каталог не нужен — опция MDBX_NOSUBDIR: переданный путь используется напрямую как файл данных, а рядом появится файл с суффиксом -lck.

Транзакция

Внутри окружения создаётся транзакция (mdbx_txn_begin()): read-write или read-only; write-транзакции могут быть вложенными. Транзакция может использоваться только одним потоком одновременно. Транзакции обязательны даже для чтения — они дают консистентный снимок данных.

Таблица

Внутри транзакции открывается таблица (mdbx_dbi_open()) — key-value пространство внутри окружения. Если используется единственная таблица, имя может быть NULL. Для именованных таблиц нужен флаг MDBX_CREATE (создание, если нет), а после mdbx_env_create() и до mdbx_env_open() — mdbx_env_set_maxdbs() с бюджетом именованных таблиц.

Одна транзакция может открыть несколько таблиц. Таблицы следует открывать один раз — первой транзакцией процесса.

Чтение и запись

Внутри транзакции mdbx_get() и mdbx_put() работают с парами ключ-значение. Пара — две структуры MDBX_val (аналог POSIX struct iovec): iov_base (указатель на данные) и iov_len (длина). Нюанс против LMDB: libmdbx поддерживает ключи и значения нулевой длины.

Так как libmdbx эффективна (обычно zero-copy), данные в MDBX_val могут быть отображены из памяти прямо с диска: смотрите, но не трогайте (и не free()). После закрытия транзакции значения использовать нельзя — скопируйте, если нужно сохранить.

Курсоры

Для более мощной работы — курсор (mdbx_cursor_open() внутри транзакции): mdbx_cursor_get(), mdbx_cursor_put(), mdbx_cursor_del().

mdbx_cursor_get() позиционируется по запрошенной операции (и, для некоторых, по ключу): чтобы перечислить все пары — MDBX_FIRST, затем MDBX_NEXT до конца; чтобы получить все ключи от заданного — MDBX_SET. Полный список операций — в справочнике.

mdbx_cursor_put() либо позиционирует курсор по ключу сам, либо использует операцию MDBX_CURRENT для текущей позиции (ключ обязан совпадать с текущим).

Коммит и откат

Транзакция фиксируется mdbx_txn_commit() либо полностью отбрасывается mdbx_txn_abort().

Важно (отличие от LMDB): открытые курсоры можно переиспользовать и нужно закрывать явно — независимо от того, были они открыты в read-only или write-транзакции. Это устраняет неоднозначность и класс ошибок use-after-free/double-free.

Важно (отличие от LMDB): дескрипторы открытых таблиц становятся доступны другим транзакциям немедленно — независимо от того, будет ли транзакция прервана или сброшена.

Одновременно могут быть активны несколько read-транзакций, но только одна write: попытки начать следующую будут блокироваться до коммита/отката текущей. На чтение это не влияет.

Потоки и процессы

  • Не открывайте одну БД дважды в одном процессе — libmdbx отслеживает и предотвращает это. Разделяйте открытое окружение между всеми потоками. Совместимость с LMDB (разрешающей multi-open) — опция MDBX_DBG_LEGACY_MULTIOPEN через mdbx_setup_debug() до вызова остальных функций; восстановление блокировок может давать паузы.
  • Не используйте окружение в потомке после fork() — libmdbx проверяет это в критических точках. При необходимости — mdbx_env_resurrect_after_fork().
  • Не более одной транзакции на поток. Нарушения детектируются кодами MDBX_TXN_OVERLAPPING, MDBX_BAD_RSLOT, MDBX_BUSY — если не включена опция MDBX_NOSTICKYTHREADS (передача транзакций между потоками; с ней вы должны точно понимать, что делаете). Write-транзакция обязана быть завершена в породившем её потоке — иначе MDBX_THREAD_MISMATCH.

Дубликаты ключей (multimap)

mdbx_get() возвращает только первое значение ключа. Для нескольких значений откройте таблицу с флагом MDBX_DUPSORT: тогда mdbx_put() добавляет значение к ключу (а не заменяет), mdbx_del() учитывает поле значения (точечное удаление), а курсоры получают дополнительные операции обхода дубликатов.

Оптимизация чтения

Если read-транзакции часто начинаются и прерываются — используйте mdbx_txn_reset() (освобождает старые копии данных) + mdbx_txn_renew() (возобновление); курсоры — mdbx_cursor_renew()/mdbx_cursor_close(). Окончательно завершить транзакцию — mdbx_txn_abort().

Очистка

Все созданные курсоры закрываются mdbx_cursor_close() (см. важное выше). Дескрипторы таблиц обычно оставляют открытыми: закрытие делает их недоступными всем транзакциям окружения — не закрывайте дескриптор, пока его использует хотя бы одна транзакция.

Куда дальше

  • Управление размером БД: mdbx_env_set_geometry().
  • Bulk-загрузка: MDBX_MULTIPLE, MDBX_APPEND.
  • Ускорение LIFO-переиспользованием на write-back носителях: MDBX_LIFORECLAIM.
  • Оценка объёма диапазонных запросов: mdbx_estimate_*.
  • Разрешение «БД заполнена» из-за долгих читателей: mdbx_env_set_hsr().
  • Последовательности и canary-маркеры: mdbx_dbi_sequence(), MDBX_canary.
  • Режимы долговечности — компромиссы надёжность/скорость.
  • Учебник: Том I — систематическое изложение.