Skip to content

Ограничения и подводные камни

Смежные: Обзор · Первые шаги · Режимы долговечности · Учебник: Том V «Экспертные темы»

Ограничения количественных параметров — в Обзоре; здесь — поведенческие ограничения, операционные риски и способы их обхода.

Многоверсионность и «долгоживущие» читатели

Самый важный операционный нюанс libmdbx. Механика: каждая write-транзакция строит новую версию изменяемых страниц (MVCC на базе copy-on-write); читатели работают со снапшотами и не блокируются. Страницы, освободившиеся в новых write-транзакциях, не могут быть переиспользованы, пока на них смотрит хоть один активный старый снапшот. Поэтому активная модификация данных при параллельном долгом чтении увеличивает рабочий набор и может быстро исчерпать свободное место (MDBX_MAP_FULL).

Типичные источники долгих читателей: горячий backup на медленное хранилище, отладка клиентского приложения с активной read-транзакцией, приостановленные (SUSPEND/ptrace) процессы с открытым чтением.

Инструменты смягчения — уже доступны:

  • Парковка транзакций — mdbx_txn_park()/mdbx_txn_unpark(): припаркованный читатель помечается псевдо-TID, перестаёт влиять на водораздел переиспользования и может быть вытеснен ( ousted ) с автоматическим перезапуском при обращении.
  • Handle-Slow-Readers — mdbx_env_set_hsr(): колбэк вызывается, когда БД заполнилась из-за отставших читателей; может подождать, асинхронно прервать транзакцию читателя (возврат 1) или зафиксировать гибель процесса (возврат 2+).
  • MDBX_LIFORECLAIM — LIFO-переиспользование укорачивает цикл оборота страниц и смягчает последствия на write-back носителях.
  • Выселение припаркованных читателей автоматически перед исчерпанием места (вместе с установкой нового steady-снапшота).
  • Читайте второстепенные данные на второстепенной копии БД.

Roadmap: радикальное преодоление архитектурного ограничения (переиспользование страниц независимо от старых MVCC-снапшотов — nonsequential GC recycling) — в следующей версии движка (MithrilDB), где проблема долгоживущих читателей решена полностью; частичные улучшения планируются и в ближайших релизах libmdbx.

Избегайте: остановки/блокировки потоков с активным чтением (включая отладку), abort'ов процессов с чтением (libmdbx чистит слоты читателей автоматически, но проверка не может выполняться слишком часто без ущерба производительности).

Крупные значения

  • Значения до 0x7FF00000 байт хранятся в цепочках последовательных страниц — чтение происходит напрямую, без копирования и накладных расходов.
  • Обратная сторона: запись крупного значения требует найти достаточную последовательность свободных страниц, что при фрагментации B+tree может быть дорогим или невозможным (движок может обработать весь GC или увеличить файл). Максимум скорости чтения — ценой скорости записи.
  • Что уже доступно: поиск свободных последовательностей в GC существенно ускорен (SIMD: NEON/SSE2/AVX2/AVX512); спилл и refund уменьшают фрагментацию; геометрия с автоматическим ростом помогает при нехватке последовательностей.
  • Roadmap: переработка хранения крупных значений и обхода фрагментации — в MithrilDB (где проблема снята архитектурно) и в уточнениях ближайших релизов libmdbx.

Огромные транзакции

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

Что уже доступно: функция «Big Foot» (включена по умолчанию) — крупные PNL-списки дробятся на цепочки записей последовательных txnid, исключая поиск, выделение и хранение длинных последовательностей; см. MDBX_ENABLE_BIGFOOT. Дополнительно — LIFO-политика и refund уменьшают объём бухгалтерии GC.

Roadmap: сохраняется в MithrilDB — где списки освобождённых страниц вписаны в архитектуру без поиска свободных последовательностей.

Резервирование адресного пространства

Конфигурация БД часто резервирует заметный объём виртуального адресного пространства и (потенциально) файла под рост — это не потребляет реальную память и диск. Но на 64-битных системах с малым объёмом RAM задание неадекватно большого size_upper через mdbx_env_set_geometry() может исчерпать системные ресурсы (ENOMEM). Не задавайте верхнюю границу «про запас».

Сетевые файловые системы

  • Не размещайте БД на удалённых файловых системах — даже между процессами одного хоста: это ломает файловые блокировки на некоторых платформах и синхронизацию memory mapping.
  • Исключения: эксклюзивный режим работы по сети и кооперативное read-only чтение с read-only сетевых шар.

Дочерние процессы и fork()

  • Не используйте открытое окружение в потомке после fork(). Лучший способ — не иметь открытых экземпляров libmdbx на момент fork(); build-опция MDBX_ENV_CHECKPID (по умолчанию ON на не-Windows) позволяет выявить такие ошибки раньше, но не даёт гарантий.
  • С v0.13.1 доступна mdbx_env_resurrect_after_fork() — уже доступное переиспользование уже открытого окружения в потомке, строго без наследования транзакций родителя.
  • Не вызывайте fork() одновременно из нескольких потоков.

Read-only режим

«Чистого» read-only режима нет: читателям нужна запись в LCK-файл, чтобы быть видимыми писателю. libmdbx всегда пытается открыть/создать LCK в read-write; при ошибках (EROFS, EACCES, EPERM) и запрошенном MDBX_RDONLY переключается в режим без LCK.

Один поток — одна транзакция

Поток может использовать только одну транзакцию одновременно (плюс вложенные write-транзакции вне WRITEMAP). Каждая транзакция принадлежит одному потоку; write-транзакция завершается в породившем её потоке. Нарушения возвращают MDBX_TXN_OVERLAPPING, MDBX_BAD_RSLOT, MDBX_BUSY. Опция MDBX_NOSTICKYTHREADS разрешает передачу транзакций между потоками — только для тех, кто точно понимает, что делает (дедлоки, чтение чужих данных).

Не открывайте БД дважды

Повторное открытие одной БД в одном процессе недопустимо: при отсутствии OFD-блокировок используются POSIX-блокировки, которые ломаются при многократном открытии файла одним процессом (закрытие одного экземпляра снимает все блокировки). libmdbx отслеживает и предотвращает это (MDBX_BUSY); совместимость с LMDB — mdbx_setup_debug(MDBX_DBG_LEGACY_MULTIOPEN, ...) с восстановлением блокировок (возможны паузы).

Диагностика LCK-файла

LCK-файл очищается при старте первого процесса с БД: при любых повреждениях LCK достаточно закрыть БД во всех процессах (или остановить их) и перезапустить.

  • Повреждение LCK возможно при некорректном приложении, пишущем через указатели в shared-память; переносимого способа защиты нет (LCK обновляется конкуренто и lock-free — блокировки дали бы большие накладные расходы).
  • Застрявшие читатели (после аварийного завершения программы) приводят к быстрому росту БД; libmdbx проверяет их при открытии окружения и перед ростом.
  • Застрявшие писатели чистятся автоматически (платформенно-зависимо, через robust-мьютексы); известных проблем на поддерживаемых платформах нет.