Первые шаги
Смежные: Установка и сборка · Тулинг · Ограничения · Учебник: Том 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 — систематическое изложение.