Skip to content

Improvements: что добавлено в libmdbx по сравнению с LMDB

Публичная редакция для амальгамированного пакета. Каталог доработок libmdbx над LMDB: возможности, расширения API, механизмы движка, исправления и оптимизации. Смежные: architecture.ru.md, deep-dive.ru.md. Это попытка разложить по полочкам, чем именно libmdbx отличается от той базы, из которой выросла. История начиналась с форка, но довольно быстро перестала быть «LMDB с правками»: сегодня это самостоятельная кодовая база со своим характером. Ниже — её послужной список: что добавлено, что переделано, что вылечено и что ускорено, вплоть до того, как именно движок ищет свободные страницы (спойлер: там векторные инструкции).

Точность здесь важнее краткости, но и краткость важна: у пунктов указаны версии, начиная с которых изменение стало заметным, — как ориентиры, а не как строгая историческая граница.


1. Сводная карта

Категория Краткая суть Ключевые пункты
A. Надёжность Устранены дефекты, до сих пор живущие в LMDB §2
B. Модель данных и формат Длиннее ключи, нулевая длина, формат независим от разрядности §3
C. Транзакции и конкурентность Парковка/вытеснение, клонирование, fork, HSR §4
D. GC и размер БД Big Foot, LIFO/FIFO, авто-компактификация, дефрагментация §5
E. Долговечность SAFE_NOSYNC, steady-точки, троика мета, восстановление §6
F. Движок B+tree Умные split, merge-тактика, мультизначения, оценки §7
G. C API Расширенные операции, информация, опции, кэш §8
H. C++ API Полный типобезопасный слой §9
I. Утилиты и диагностика chk/defrag/copy/dump, статистика, UUID §10
J. Оптимизации SIMD, branchless, prefault, спилл, сортировки §11
K. Переносимость Платформы, блокировки, TLS, endianness §12
L. Сборка/дистрибуция Amalgamation, CMake/Make, LTO, лицензия, Conan §13
M. Экосистема Сотни проектов, биндинги, награды, MithrilDB §14

Хронологическая таблица по версиям — §15.


2. Надёжность: устранённые дефекты, унаследованные от LMDB

Значительная часть изменений — это исправление дефектов, которые в LMDB живут годами и десятилетиями и воспроизводятся только в определённых сочетаниях условий, что делает их особенно коварными. Ниже перечислены те из них, которые удалось идентифицировать и закрыть.

Известные и публично зафиксированные:

  • Утечки страниц БД и ошибочная статистика таблиц. Последствия двоякие: теряется пространство файла (страницы выпадают из оборота), а метрики роста становятся недостоверными — приложение видит «раздувание» базы, которого на самом деле нет.
  • Segfault'ы в нескольких условиях: повреждённые базы, крайние случаи позиционирования курсоров, использование хендла таблицы 0 в читающей транзакции, обход деревьев после частичной порчи. Устранение сводится к строгим проверкам на границах данных.
  • Неоптимальная стратегия слияния страниц при удалении. LMDB при опустошении листа всегда сливала его с наименее заполненным соседом; исправлено на тактику «предпочесть уже изменённую (грязную) страницу» — подробнее в §5.5/§7. Выигрыш — до 50% на массовых удалениях.
  • Обновление существующей записи с изменением размера данных (в том числе в multimap). В определённых случаях LMDB может молча терять данные при больших значениях; в libmdbx этот путь переработан так, чтобы либо корректно переразместить значение, либо вернуть ошибку.
  • Повреждение БД в режиме DUPFIXED при длинных или нечётных мультизначениях (LEAF2-страницы). Причина — резервирование места без учёта возможного переполнения страницы. Ошибка присутствовала в LMDB более 11 лет и была унаследована; в libmdbx исправлена (0.12.10).
  • «Реинкарнация» удалённой под-БД и неявное удаление таблиц через операции над @MAIN. Раньше удалённая таблица могла «оживать» в новой транзакции; теперь жизненный цикл таблицы отслеживается явно.
  • Гонки при открытии DBI-хендлов и при старте транзакции параллельно с созданием дескриптора таблицы. Устраняются синхронизацией импорта хендлов в транзакцию.
  • Циклирование обновления GC при коммите (расходимость, когда повторная обработка не сходится к стабильному состоянию). Устранено корректирующей обратной связью в цикле обновления.
  • Ложное MDBX_CORRUPTED при невыровненном доступе к 64-битным полям — в том числе на ARM и после #pragma pack; закрыто аккуратными работами с выравниванием.
  • Ошибки копирования БД на NFS/CIFS/SMB (конфликт fcntl/flock, EAGAIN/EWOULDBLOCK): функция копирования теперь корректно работает с сетевыми ФС.
  • Потеря содержимого таблицы при abort вложенной транзакции, в которой таблица была удалена (0.14.x): состояние таблицы теперь восстанавливается вместе с остальными данными.
  • Некорректное закрытие DBI-дескриптора изменённой таблицы: могло создавать таблицу с пустым именем, терять страницы или портить ссылку на корень дерева; теперь возвращается ошибка.
  • «Воскрешение» закрытых курсоров вложенных транзакций: приводило к утечкам памяти и использованию-после-освобождения; жизненный цикл курсоров отслеживается жёстче.

Системные гарантии надёжности:

  • Целостность при асинхронной неупорядоченной записи (SAFE_NOSYNC). В отличие от MDB_NOSYNC, база не разрушается при системном сбое: выполняется откат к последнему steady-коммиту. Для поведения, в точности совпадающего с LMDB, предусмотрен отдельный режим MDBX_UTTERLY_NOSYNC — выбор делается явно.
  • Контроль по boot_id при принятии решения об откате слабых мета-страниц — в том числе при работе внутри LXC-контейнера.
  • Защита от некогерентности unified page/buffer cache (Linux, issue #269): полный workaround, внесённый в сериях 0.11.5–0.11.6.
  • Проверка согласованности файловых систем. Для неподходящих ФС библиотека либо отказывает с явной ошибкой, либо предупреждает; эксклюзивный режим на сетевых шарингах разрешён, кооперативный read-only — тоже.
  • Чистка «зависших» читателей при открытии и перед ростом БД, а также авто-очистка «зависших» писателей (признак устаревшей регистрации).
  • MDBX_EMULTIVAL при неоднозначном обновлении/удалении. Вместо тихого (и потому опасного) поведения возвращается явная ошибка «несколько значений».
  • Защита от двойного открытия одной БД в пределах процесса: отслеживание и восстановление POSIX-блокировок после fork; для совместимости со старым поведением предусмотрен legacy-режим.
  • Возврат ENOLCK на WSL1, где работа невозможна в принципе: явный отказ предпочтительнее молчаливого повреждения данных.

3. Модель данных и формат

  • Ключи более чем в два раза длиннее, чем в LMDB: до ~½ страницы (2022 байта при странице 4 КБ, 32742 при 64 КБ) против 511 байт. Лимиты вынесены в API (mdbx_limits_*, mdbx_env_get_maxkeysize() и др.). Практический эффект — для многих схем отпадает необходимость хешировать длинные ключи или вводить отдельные таблицы-справочники.
  • Нулевая длина ключей и значений. У LMDB ключ обязан быть ненулевой длины. Возможность хранить «пустые» сущности упрощает представление бинарных протоколов и разреженных структур.
  • Формат БД одинаков для 32- и 64-битных сборок — зависит только от endianness платформы. Файл, созданный на 64-битной машине, открывается на 32-битной и наоборот; это существенно для переноса баз между средами.
  • Мультизначения. Поддерживаются три формы хранения: плотные dupfix-страницы фиксированной длины, вложенные деревья и суб-страницы; выбор делается автоматически по числу и размеру значений. Лимиты и глубина вложенности доступны через API (dupsort_depthmask, count_ex).
  • Последовательности (sequences) и три персистентных 64-битных маркера (vector-clock) — штатный инструмент для генерации монотонных номеров и меток вида «кто и когда менял данные».
  • UUID базы в поле mi_dxbid: позволяет отличать разные копии одной базы, а не только проверять совпадение сигнатуры формата.
  • Внутренние размеры узлов уточнены: в ряде случаев меньше overflow-страниц и выше лимиты ключей для одного и того же размера страницы.
  • Непечатные и пустые имена таблиц обрабатываются корректно (вплоть до бинарных имён).
  • Номер последней модифицирующей транзакции для каждой таблицы (per-subDB last-update txnid) — основа для дельта-синхронизации и репликационных схем поверх базы.

Почему это важно. Модель данных — это контракт: его нельзя «подклеить» поверх готовой базы. Удлинение ключей, поддержка нулевой длины и единый 32/64-битный формат меняют не отдельные функции, а сам допустимый набор данных и совместимость файлов. Поэтому эти пункты вынесены отдельно от мелочей API: это фундамент, на котором строятся остальные доработки.


4. Транзакции и конкурентность

  • Парковка читающих транзакций (mdbx_txn_park/unpark) с флагами PARKED/AUTOUNPARK/ OUSTED и авто-перезапуском вытесненных (restart_if_ousted). Припаркованная транзакция освобождает слот читателя: её снапшот перестаёт удерживать переиспользование страниц, а при возобновлении транзакция продолжает работу с того же места.
  • Вытеснение (ousting) припаркованных читателей. Если писателю не хватает пространства, он атомарно переводит слот из состояния PARKED в OUSTED; читатель при следующем обращении либо перезапускается на свежем снапшоте, либо получает явный код результата.
  • Handle-Slow-Readers (HSR) — колбэк для разрешения переполнения, вызванного долгими читателями (развитие старого mdbx_env_set_oomfunc). Вместе с парковкой образует законченный инструментарий работы с «застрявшими» снапшотами.
  • Клонирование читающих транзакций (mdbx_txn_clone): несколько позиций на одном снапшоте.
  • mdbx_env_resurrect_after_fork() — безопасное переоткрытие окружения в дочернем процессе после fork(), без полного закрытия и повторного открытия.
  • Вложенные транзакции радикально переработаны: быстрый путь для «чистой» вложенной транзакции, ленивое затенение курсоров, корректная судьба созданных и удалённых таблиц, поддержка в C++ API.
  • Вложенные read-only транзакции и «фейковые» read-only — для единообразия API.
  • Расширенный набор операций над транзакцией: mdbx_txn_refresh(), mdbx_txn_checkpoint() (коммит без снятия блокировки), mdbx_txn_commit_embark_read() (коммит, сразу переходящий в читающую транзакцию), mdbx_txn_amend() (запись на основе читательского снапшота), mdbx_txn_rollback() (abort + restart без потери блокировки).
  • mdbx_txn_break() — явная пометка транзакции как повреждённой.
  • NOSTICKYTHREADS вместо LMDB'шного MDBX_NOTLS. Транзакцию можно передавать между потоками; нарушение дисциплины детектируется и возвращается явными кодами (MDBX_THREAD_MISMATCH, MDBX_TXN_OVERLAPPING, MDBX_BAD_RSLOT, MDBX_BUSY). Это важно для корутин и пулов потоков, где «прилипание» транзакции к одному потоку неприемлемо.
  • Явная регистрация/дерегистрация потоков-читателей (mdbx_thread_register/unregister).
  • Управление основной блокировкой lock/unlock/upgrade/downgrade для сложных сценариев координации писателей.
  • Дисциплина курсоров: все курсоры можно переиспользовать и требуется закрывать явно — это устраняет целый класс use-after-free и двойных освобождений. Соответствующие операции: mdbx_cursor_create/bind/unbind, mdbx_txn_release_all_cursors[_ex].
  • Позиционирование курсоров операциями <, <=, ==, >=, > (в том числе для пар ключ-значение); MDBX_SET_LOWERBOUND, MDBX_SET_UPPERBOUND, mdbx_cursor_compare (аналог оператора <=>), mdbx_cursor_scan[_from], on_first_dup/on_last_dup.
  • Курсоры на одну таблицу в разных read-транзакциях допустимы (multi-cursor API).
  • mdbx_preopen_snapinfo() — получение информации о базе без её открытия.

Общий замысел. LMDB трактует транзакцию как короткий отрезок работы в одном потоке; libmdbx — как объект с собственным жизненным циклом. Отсюда набор операций: транзакцию можно приостановить (парковка), размножить (clone), перенести в другой поток (NOSTICKYTHREADS), пережить fork() и превратить read-в-write на лету. Каждый режим покрывает конкретный паттерн реальных приложений.


5. Переиспользование страниц (GC), рост и размер БД

  • Big Foot (0.12.1) — разбиение больших списков retired-страниц на цепочки записей GC. Устраняет требование искать и выделять длинные последовательности свободных страниц для хранения самих списков; критично для транзакций, утилизирующих миллионы страниц (например, в нагрузках Ethereum-экосистемы).
  • Early GC Cleanup (0.14.x) — утилизированные записи GC удаляются как можно раньше, а не только на коммите; открывает путь к дефрагментации и нелинейной переработке GC.
  • LIFO-политика переиспользования (MDBX_LIFORECLAIM) при FIFO по умолчанию. LIFO берёт самые свежие освобождения: страница циркулирует по минимально короткому кругу и не успевает выпасть из дискового write-back кэша. На системах с таким кэшем заметно растёт скорость записи.
  • Автоматическая подстройка размера БД на лету — рост и сокращение — через параметры геометрии lower/now/upper/growth_step/shrink_threshold.
  • Непрерывная компактификация с нулевыми накладными расходами: возврат свободного хвоста в неразмеченное пространство (refund) на каждом коммите + усечение файла при накоплении (implicit shrink, stockpile_gap, madvise(MADV_DONTNEED/REMOVE)). Работа идёт попутно с обычными коммитами, без отдельного прохода.
  • Явная дефрагментация (mdbx_env_defrag, утилита mdbx_defrag) с контролем целей, времени и допустимой «отдачи» (backlash).
  • Динамические лимиты: rp_augment_limit (авто-подстройка от размера БД), gc_time_limit (лимит времени поиска последовательностей), лимиты грязных страниц (txn_dp_limit — авто от объёма ОЗУ) и спилл-деноминаторы.
  • Авто-слияние записей GC и оптимизированный pnl_merge; профилирование работы GC (MDBX_ENABLE_PROFGC).
  • Диагностика GC: mdbx_gc_info() (состояние, гистограммы, итерация записей) и счётчик gcrtime — время, потраченное на поиск и переработку.
  • Refund/loose-страницы: возврат освобождённых страниц внутри текущей транзакции без обращения к GC (опции MDBX_ENABLE_REFUND, MDBX_opt_loose_limit, dp_reserve_limit).
  • RKL — отслеживание идентификаторов записей GC (интервалы + списки) с «резервированием со взвешенным запасом»: одно-проходное обновление GC с оценкой сложности от O(1) до O(log N).
  • Разреженные и lock-free наборы DBI-хендлов (MDBX_ENABLE_DBI_SPARSE, MDBX_ENABLE_DBI_LOCKFREE) — снижение накладных расходов при большом числе таблиц.
  • Счётчики пространства в mdbx_txn_info: dirty/leftover/retired/limit для читающих и пишущих транзакций — рабочий инструмент диагностики влияния «долгих читателей».

Как это складывается в систему. GC в libmdbx — не сборщик мусора в классическом смысле, а постоянный учёт свободных страниц внутри самого файла. Поэтому важны не отдельные механизмы, а их связка: политика FIFO/LIFO выбирает порядок записей GC по txnid (какие освобождения вернуть в первую очередь); refund возвращает хвост без записи в GC; Big Foot упаковывает гигантские списки освобождений; лимиты (rp_augment_limit, gc_time_limit) не дают поиску свободного места съесть весь коммит. В сумме это делает переработку свободного списка при огромных транзакциях предсказуемым и настраиваемым процессом.


6. Долговечность и синхронизация

  • Три мета-страницы + метод фиксации «Troika» (0.12.1). Два устойчивых снапшота плюс хвостовой слот для перезаписи; двухфазное обновление с минимумом барьеров памяти, сравнений и условных переходов. У LMDB мета-страниц две, и их обновление — источник тонких гонок.
  • Явные режимы синхронизации: MDBX_SYNC_DURABLE, MDBX_NOMETASYNC, MDBX_SAFE_NOSYNC (переименование MDBX_NOSYNC), MDBX_UTTERLY_NOSYNC; MDBX_MAPASYNC объявлен устаревшим. Названия режимов отражают степень риска — это снижает вероятность случайного выбора «недолговечного» режима.
  • Steady и weak мета-страницы. Понятие устойчивой точки формализовано; при нехватке пространства библиотека автоматически формирует новый steady-point.
  • Автоматическая синхронизация по порогам и/или таймауту с дешёвым polling: mdbx_env_set_syncbytes/syncperiod, MDBX_opt_sync_bytes/period, presync_threshold, асинхронные mdbx_env_sync[_ex|_poll] с предварительной проверкой и повторной попыткой.
  • Динамический выбор способа записи (MDBX_opt_writethrough_threshold): сквозная запись (O_DSYNC) против записи с последующим fdatasync() — в зависимости от числа страниц и задержки канала хранения.
  • Prefault-запись (MDBX_opt_prefault_write_enable) — упреждающая запись страниц, выделенных для WRITEMAP, чтобы устранить page-fault'ы и чтения с диска при первом обращении.
  • Восстановление без WAL: выбор последней цельной мета-страницы, режим open_for_recovery и переключение на заданную мета-страницу; начиная с 0.12.7 recovery-проверки не изменяют базу.
  • Диагностические коды MDBX_WANNA_RECOVERY / MDBX_MVCC_RETARDED.
  • Контроль некогерентности unified page cache (см. §2).
  • На macOS/iOS используется fcntl(F_FULLFSYNC) по умолчанию — единственный способ гарантировать долговечность при сбое питания; опция MDBX_APPLE_SPEED_INSTEADOF_DURABILITY позволяет пожертвовать долговечностью ради скорости.

Наблюдение. Долговечность — область, где подход отличается сильнее всего. Режимы синхронизации названы так, чтобы из названия была понятна цена вопроса (SAFE/UTTERLY), steady-точка формализована, а восстановление после сбоя сведено к воспроизводимой процедуре: выбрать последнюю цельную мета-страницу, при необходимости аккуратно переключить тройку под эксклюзивной блокировкой.


7. Движок B+tree и страницы

  • Split с «auto-appending» (0.10.0) — при вставке упорядоченных последовательностей ключей страницы заполняются плотнее; split «по середине» — дерево остаётся сбалансированным в среднем, а не «перекошенным» в сторону вставок.
  • Тактика слияния при удалении. При опустошении листа слияние выполняется преимущественно с уже изменённой (грязной) страницей; если статусы одинаковы — с наименее заполненной. Снижает WAF, до 50% выигрыша на массовых удалениях. Управляется prefer_waf_insteadof_balance и merge_threshold (с 0.14.x по умолчанию 33%).
  • Прозрачный спилл (0.10.0) — грязные страницы переводятся в состояние «готовы к выгрузке» без последующих изменений; LRU-политика с приоритетом для overflow-страниц; спилл учитывает размер large/overflow-страниц; MDBX_TXN_NIPPED позволяет приостановить спилл при обработке GC.
  • Массовые операции. Удаление «гроздьями» (mdbx_cursor_bunch_delete, delete_range) вырезает целые страницы и ветви вместо поштучного удаления; пакетное чтение (mdbx_cursor_get_batch); пакетная работа с мультизначениями (MDBX_GET/PUT_MULTIPLE, SEEK_AND_GET_MULTIPLE, put/seek samelength, batch put).
  • Оценка объёма диапазонных запросов (mdbx_estimate_range/distance/move, MDBX_EPSILON): оценка по общим страницам стеков курсоров, без сканирования данных.
  • Проверка страниц «на лету»: улучшенная онлайн-валидация; опция MDBX_VALIDATION для работы с повреждёнными или недоверенными базами; обнаружение повреждений по полю parent-page-txnid.
  • Параметры плотности: MDBX_opt_subpage_* (лимит суб-страниц и резерв) и split_reserve.
  • Трекинг курсоров в пишущих транзакциях (repoint/shadow) и mdbx_is_dirty() для того, чтобы не копировать данные с грязных страниц.
  • Минимизация чтения листовых страниц при удалении таблиц и вложенных деревьев.

Наблюдение. B+tree — простая структура, но вся разница в деталях перестроек. Auto-appending split позволяет писать упорядоченные последовательности почти без потери места; слияние с грязным соседом экономит записываемые страницы (WAF); bunch-delete вырезает поддеревья вместо поштучного стирания; оценка диапазонов избавляет от сканирования. По отдельности каждый пункт — мелочь, вместе они дают заметную разницу в бенчмарках и в реальных нагрузках.


8. C API: расширения

  • Единый API опций mdbx_env_set_option/get_option с набором MDBX_opt_*: max_db, max_readers, sync_bytes, sync_period, rp_augment_limit, loose_limit, dp_reserve_limit, txn_dp_limit, txn_dp_initial, spill_max_denominator, spill_min_denominator, spill_parent4child_denominator, merge_threshold, prefer_waf_insteadof_balance, writethrough_threshold, prefault_write_enable, gc_time_limit, split_reserve, subpage_limit, subpage_room_threshold, subpage_reserve_prereq, subpage_reserve_limit, presync_threshold.
  • Расширенные CRUD: put с получением предыдущего значения; обновление/удаление конкретного мультизначения; MDBX_UPSERT, MDBX_ALLDUPS, MDBX_APPEND/DUP, MDBX_NOOVERWRITE; upsert всех дубликатов; reserve; empty-data в MDBX_MULTIPLE.
  • «get-cached»: mdbx_cache_get[_SingleThreaded] — ленивый поиск по версионным меткам страниц со статусами HIT/CONFIRMED/REFRESHED/DIRTY/BEHIND/UNABLE/RACE; разделяемый lock-free. Повторное чтение «горячего» ключа превращается из полного спуска по дереву в пару сравнений.
  • Геометрия и лимиты: mdbx_env_set_geometry, mdbx_limits_* (keysize/valsize/pairsize ...4page_max, минимальные границы), mdbx_default_pagesize, mdbx_get_sysraminfo.
  • Информация: mdbx_env_info_ex (геометрия, меты, meta[3], UUID, статистика операций со страницами), mdbx_txn_info (включая scan_rlt), mdbx_dbi_flags_ex (состояние DIRTY/STALE/FRESH/CREAT), mdbx_enumerate_tables, mdbx_cursor_count_ex.
  • Транзакции: весь набор из §4; mdbx_txn_commit_ex с метриками задержек; mdbx_txn_copy2pathname/fd (копия из транзакции).
  • Таблицы: mdbx_dbi_rename[_2], mdbx_dbi_sequence, маркеры mdbx_canary_*, API mdbx_drop, отложенная инвалидация хендлов удалённых таблиц (0.14.3+), MDBX_DB_ACCEDE.
  • Окружение: mdbx_env_delete (мультипроцессное удаление), mdbx_env_warmup, mdbx_env_chk (проверка изнутри библиотеки), mdbx_env_defrag, mdbx_env_sync_poll, режимы exclusive/read-only/без-LCK, mdbx_env_get_path[_W], mdbx_set_panic, пользовательский контекст для транзакций и курсоров.
  • Преобразования ключей: value-to-key для чисел (int32/int64, float/double, JSON-integer), mdbx_key_from_* — рекомендуется вместо кастомных компараторов, чтобы базу можно было проверять штатным mdbx_chk.
  • Логирование: callback без vprintf (удобно для привязок к другим языкам), mdbx_assert_fail в публичном API, настройка уровня через переменные окружения, уровни MDBX_LOG_DEBUG/TRACE для ошибок API.
  • Диагностика времени выполнения: mdbx_get_sysraminfo, page-op статистика через mdbx_env_info_ex, MDBX_ENABLE_PGET_STAT (счётчик обращений к страницам).

Наблюдение. Практически каждый внутренний механизм имеет либо настройку (MDBX_opt_*), либо измеритель (txn_info, page-op счётчики, gc_info), либо и то и другое. Это сознательная цена (интерфейс больше), но именно она позволяет диагностировать долгие читатели, WAF и churn GC, не вскрывая исходники.


9. C++ API

  • Полный типобезопасный RAII-слой: env/env_managed, txn/txn_managed, cursor/cursor_managed, map_handle, slice/buffer<> (полиморфные аллокаторы C++17, политики владения, inplace_storage_size_rounding).
  • Иерархия исключений, сопоставленная кодам C API; make_broken(); конвенции [[nodiscard]]; C++20 concepts при наличии.
  • Типизированные map-операции, mdbx::pair, трансляция ключей/значений (включая value2key).
  • Кодировщики: hex/base58/base64 (высокопроизводительный base58 по черновику RFC), is_printable, проверки UTF-8, безопасные middle/резервирование.
  • mdbx::comparator, default_comparator, estimate_result, extra_runtime_option, геометрия с fluent-сеттерами, метрики задержек коммита.
  • Вложенные пишущие транзакции, buffer append/reserve, get_/set_context, move/copy assignment для управляемых классов, withdraw_handle.
  • Явная инстанциация шаблонов внутри библиотеки — быстрее сборка потребителей.

10. Утилиты и диагностика

  • mdbx_chk: глубокая проверка (мета/троика, деревья, порядок ключей, GC, scopes, гистограммы заполнения страниц и мультизначений), проверка по указанной мета-странице, переключение мета-страниц, опции -u/-U (warmup), уровень подробности, «выживание» при повреждённых деревьях (обходит испорченные ветки вместо падения).
  • mdbx_copy: горячее резервное копирование (в том числе в pipe), copy-with-compaction (обнуление неиспользуемых промежутков), опции -d/-p/-f, overwrite.
  • mdbx_defrag: явная дефрагментация с целями и лимитами.
  • mdbx_dump/mdbx_load: полная поддержка атрибутов, компактный режим -c (однократные ключи), purge -p, batch-insert -b, лимиты -L, плотность -d, геометрия -G.
  • mdbx_drop, mdbx_stat (включая -p — page-op статистику и все счётчики), mdbx_test (стохастические сценарии, --geometry-jitter, --numa, --pagesize, --loglevel), stochastic.sh, battery-tmux.sh.
  • Версия/сборка: VERSION.json, SOURCE_DATE_EPOCH, MDBX_BUILD_TIMESTAMP, MDBX_BUILD_METADATA, вывод options: для совместимости хоста/контейнеров.

11. Оптимизации производительности

11.1. Микрооптимизации движка

  • SIMD-поиск последовательностей свободных страниц: ядра scan4seq_* для SSE2/NEON/AVX2/AVX512 — ускорение ×4/×8/×16 (0.12.1); выбор ядра на этапе выполнения (scan4seq_resolver), поэтому одна сборка работает и на старом x86, и на современном AVX-512 сервере; NEON починен для ARM64-Windows.
  • Branchless бинарный поиск (bsearch/lower_bound на CMOV) с обходом бага CLANG для x86; позднее — встраивание кода встроенных/дефолтных компараторов (0.14.2): поиск по числовым ключам сводится к последовательности безусловных инструкций.
  • Сортировки: адаптивная бинпоиск-сортировка, radix-sort (LSB-first, 2×16-бит цифры), сортировочные сети для n=3..8, branch-free compare-swap; быстрые сортировки списков страниц (PNL/DPL); порог переключения radix (MDBX_RADIXSORT_THRESHOLD).
  • Лениво-сортируемый список грязных страниц (DPL) с сортировкой по требованию; оптимизированный dpl_append.
  • C11-атомики для слабых моделей памяти (ARM/AArch64/PPC/MIPS/RISC-V): acquire/release, CAS, протокол safe64 для чтения 64-битных значений; проверка атомарности на этапе сборки.
  • Рациональная разметка ветвлений и функций: pure/const, __cold/__hot, __always_inline, likely/unlikely (в исходниках — тысячи расставленных подсказок).
  • __builtin_cpu_supports для диспетчеризации SIMD (MDBX_HAVE_BUILTIN_CPU_SUPPORTS).
  • Минимизация системных вызовов: отказ от pwritev для одиночных записей, объединение регионов записи, избегание лишних msync, отказ от copy_file_range на дефектных ядрах, fallocate против SIGBUS, fcntl64 для блокировок.
  • Prefault-запись и mincore-отслеживание резидентности страниц для предотвращения page-fault'ов в WRITEMAP.
  • Отсутствие операций с плавающей точкой и зависимости от libm (0.14.x); 16.16 fixed-point представление для времени и порогов.
  • Атрибуты TLS (tls_model("local-dynamic")) и аккуратная работа с деструкторами TLS.
  • -fno-semantic-interposition — снижение накладных расходов на вызовы собственных функций.

Наблюдение. Разница в производительности складывается из сотен мелких правок: часть операций быстрого пути переведена на CMOV, сортировка списков страниц ускорена ради того, чтобы спилл не простаивал, а SIMD-ядра поиска последовательностей дают ×4–×16 на конкретной операции. По отдельности каждая правка незначительна, совокупность обеспечивает заявленный выигрыш в CRUD-бенчмарках.

11.2. Алгоритмические улучшения

  • Прозрачный спилл + LRU (см. §7), refund/loose, авто-слияние записей GC, одно-проходное обновление GC через RKL (см. §5).
  • Ускорение обновления GC для огромных транзакций (сценарии Ethereum/Erigon) — 0.11.3.
  • Auto-appending split и «split по середине»; слияние с грязным соседом (см. §7).
  • Динамические эвристики: авто-подстройка dp_limit от объёма ОЗУ, rp_augment_limit от размера БД, автоподбор размера страницы и геометрии по умолчанию.

11.3. Синхронизация/параллелизм

  • Wait-free читатели без атомик на пути чтения; lock-free сканирование таблицы читателей; кэширование старейшего читателя.
  • OFD-блокировки (с fallback на POSIX fcntl64), таймаутные ожидания, SysV/semaphore варианты, режим без LCK-файла.
  • Файловые блокировки на Windows — осознанный выбор вместо именованных мьютексов LMDB. Причины и следствия:
  • LockFileEx() позволяет размещать базу на сетевых дисках (именованные мьютексы — локальная сущность и для сетевых шарингов бесполезны);
  • файловые блокировки дают защиту от некомпетентных действий пользователя (совместно с эксклюзивным режимом открытия) — защита от класса ошибок, приводящих к повреждению БД;
  • цена — производительность: в ядре Windows файловые блокировки реализованы плохо, поэтому в наивных бенчмарках с множеством мелких транзакций libmdbx может отставать от LMDB (об этом прямо сказано в FAQ проекта и ChangeLog 0.12.3);
  • это сознательный компромисс «надёжность и переносимость против скорости».
  • Overlapped/async запись на Windows (WriteGather, небуферизованный I/O) — сокращение накладных расходов на взаимодействие с ОС.

12. Переносимость и платформы

  • Платформы: Linux, Windows, macOS/iOS, Android, Harmony OS, Haiku, FreeBSD, NetBSD, OpenBSD, DragonFly, Solaris/OpenIndiana/OpenSolaris, Plan 9/9P (эксклюзивный режим), WSL2 (и корректный отказ на WSL1); инструментальные цепочки — GNU Make + CMake + MinGW + MSVC + CLANG + GCC + Elbrus/LCC.
  • Слабые модели памяти (см. §11.1); big-endian и нестандартные размеры страницы; большие БД (>4 ГБ) из 32-битного кода.
  • Обработка специфики ОС: workaround'ы для Wine, DrvFs, NFS/CIFS/SMB, CDROM, F_FULLFSYNC, GetExitCodeThread, boot_id на Windows и в LXC.
  • Защита от повторного использования pid/tid; проверки живости читателей; безопасный fork.

Наблюдение. Переносимость здесь означает не «скомпилировалось на N платформ», а «корректно работает при разных моделях памяти, endianness и экзотических ОС». Слабые модели памяти требуют аккуратных атомик; big-endian — симметричного формата страниц; 32-битные платформы — работы с большими файлами через mmap-окна.


13. Сборка, дистрибуция, лицензия

  • Amalgamated single-file дистрибутив (по образцу SQLite) с отрезаемыми dev-маркерами; make dist, пакеты, Conan-рецепт.
  • CMake (в том числе как подпроект), GNU Make, широкий набор build-опций (MDBX_WITHOUT_MSVC_CRT, MDBX_CHECKING, MDBX_VALIDATION, MDBX_ENABLE_*, MDBX_AVOID_MSYNC, MDBX_BUILD_TOOLS, MDBX_USE_OFDLOCKS).
  • Поддержка LTO (Link-Time Optimization) — отдельная подсистема сборки:
  • в CMake доступность LTO определяется автоматически для GCC, CLANG и MSVC (GCC_LTO_AVAILABLE/CLANG_LTO_AVAILABLE/MSVC_LTO_AVAILABLE) с проверкой версий (GCC ≥ 7, CLANG ≥ 5, MSVC ≥ 19);
  • включается стандартной опцией INTERPROCEDURAL_OPTIMIZATION;
  • для корректной сборки подбираются ar/nm/ranlib с LTO-plugin (или lld/ld для CLANG) — нетривиальная часть скриптов;
  • в GNU Make предусмотрены отдельные цели .static-lto для сборки утилит с -flto;
  • история правок отражает особенности интеграции: поиск GCC-инструментов для LTO, детектирование CLANG-LTO для Android, подавление ложных предупреждений -Wno-lto-type-mismatch для старых GCC при LTO-сборках.
  • Воспроизводимые сборки (SOURCE_DATE_EPOCH/MDBX_BUILD_TIMESTAMP), поддержка ASAN/UBSAN/MSAN (Valgrind/ASAN), ctest, проверки атомарности.
  • Лицензия Apache-2.0 (с 0.13), пояснения в COPYRIGHT.
  • Отдельные config-файлы для GNU Make и CMake; ограничение утечек внутренних символов.

14. Экосистема

Данные с официального сайта (libmdbx.dqdkfa.ru, на 1 сентября 2026 г.): - Всего найдено 1109 открытых проектов с 50881 звёздами, использующих libmdbx как хранилище (без учёта форков, копий и клонов); информация собирается из GitHub, SourceCraft, SourceGraph, Codeberg, GitVerse, GitFlic, mos.hub, market.dev и Yandex Cloud Search. - Крупнейшие кластеры (top-42): Ethereum-инфраструктура — Reth, Erigon, optimism, taiko-mono, ethrex, sequencer (Starknet), citrea (Bitcoin ZK rollup), rbuilder (MEV), rindexer (EVM-индексаторы); L2/блокчейн — tempo, base, Nimiq core-rs-albatross, irys; приложения — mangayomi, Isar (NoSQL для Flutter), miranda-ng (мессенджер), qiqqa (научные исследования), Monica Pass (менеджер паролей), nzbget (Usenet), Cobalt (WhatsApp API), endee (векторная БД). - Официально отслеживаемые биндинги: Rust, Go, Node.js, Zig, Python, .NET (C#), C++, Dart, Nim, Java, Haskell, Ruby, Scala. - Проект — победитель конкурса Yandex Open Source среди открытых проектов; код остаётся открытым с бесплатной поддержкой. - Стратегическое направление — MithrilDB (анонсировано в конце 2025 г.): общий API для поддержки нескольких форматов баз. В планах — поиск с кэшированием по txnid, потоковые BLOB, опциональный mmap, шифрование и сжатие на уровне движка, SWIG, репликация и кроссязыковое взаимодействие C/C++↔Rust. Старые форматы и базы будут поддерживаться, пока это нужно пользователям.


15. Хронология по версиям (ключевые вехи)

Версия (год) Ключевые доработки
2015–2017 (ReOpenLDAP/ранний) Форк; базовая надёжность, более длинные ключи, нулевые ключи
~0.9.x (2019–2021) API опций; динамические списки; переработанный спилл; refund; C11-атомики; C++ API (0.9.1); env_delete, commit_ex, SET_LOWERBOUND
0.10.x (2021) set/get_option; прозрачный спилл + LRU; auto-appending split; get_sysraminfo; DISABLE_PAGECHECKS; статистика page-op
0.11.x (2021–2022) cursor_get_batch, SET_UPPERBOUND; ускорение GC для огромных транзакций; fix incoherent page cache (#269); переезд после удаления GitHub; wchar API; C++ finalized
0.12.x (2022–2023) Big Foot; Troika; SIMD-поиск (AVX2/AVX512/SSE2/NEON); branchless bsearch; prefault-write; writethrough; merge-тактика; warmup; Windows overlapped-I/O; LCK v2; VALIDATION
0.13.x (2023–2025) Apache-2.0; парковка/вытеснение; HSR; DBI sparse/lockfree; gc_time_limit; env_chk в библиотеке; rename; cursor scan/compare; resurrect_after_fork; NOSTICKYTHREADS; subpage-опции; prefer_waf_insteadof_balance; UUID; большая серия API-расширений
0.14.x (2025–2026) Early GC cleanup; явная дефрагментация + mdbx_defrag; bunch_delete/delete_range; cache_get; txn_clone/refresh/checkpoint/amend/rollback/embark_read; вложенные read-only; gc_info; split_reserve; distance/scroll/distribute; Harmony OS/Haiku; без float/libm; MDBX_CHECKING; branchless + встроенные компараторы; presync_threshold; deferred-инвалидация DBI; стабилизация 0.14.x (0.14.3)

16. Статус и дальнейшие шаги

Каталог обновлён по данным официального сайта (экосистема, блокировки Windows, LTO). Дальнейшие шаги:

  1. Сверить пункты с кодом для категорий, где это критично (формат, опции, мета).
  2. Добавить ссылки на разделы deep-dive.ru.md, где механизмы описаны подробно.
  3. При необходимости — отдельный документ «что именно отличается в поведении по умолчанию».