- C# 54.7%
- C++ 45.2%
| Documentation | ||
| MDBX | ||
| MDBX.UnitTest | ||
| tools | ||
| .gitignore | ||
| libmdbx-dotnet.sln | ||
| LIBMDBX-FUNCTIONS.md | ||
| LICENSE | ||
| README.md | ||
| VERSION.json | ||
libmdbx-dotnet
libmdbx-dotnet — нативные P/Invoke-привязки для libmdbx и высокоуровневый API.
Что такое 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 - Множество таблиц key-value в одном файле данных, включая эффективные multimap: упорядоченные, искомые и обходимые множества значений без дублирования ключей.
-
Кроссплатформенность - Поддержка Linux, Windows, macOS, Android, iOS, FreeBSD, Solaris и других систем, совместимых с POSIX.1-2008.
-
Компактность и встраивание - Всего несколько плоских файлов исходного кода, никаких внутренних потоков и серверных процессов. Полностью пригодно для глубокого встраивания.
License
This project is licensed under the Apache License, Version 2.0.
libmdbx native library is not shipped with this assembly. The assembly will load libmdbx from the location below according to your platform and OS:
- Windows:
native/windows/x64/mdbx.dll - Linux:
native/linux/x64/libmdbx.so
Быстрый старт
Установка
Добавить источник
dotnet nuget add source https://nexus.amsoft.spb.ru/repository/nuget-public/ --name amsoft-nexus
Установка пакета
dotnet add package mdbx --version 0.14.3-39-8-develop
Установка одной командой без добавления источника
dotnet add package mdbx --version 0.14.3-39-8-develop --source https://nexus.amsoft.spb.ru/repository/nuget-public/
Посмотреть список всех доступных версий
Посмотреть в Nexus
CRUD операции
using MDBX;
using var env = MdbxEnvironment.Open("data.mdbx", maxDatabases: 4);
using var db = env.OpenDatabaseEx("users");
using (var txn = env.BeginTransaction())
{
db.Put(txn, "user:1", "{\"name\":\"Alice\",\"role\":\"admin\"}");
db.Put(txn, "user:2", "{\"name\":\"Bob\",\"role\":\"user\"}");
txn.Commit();
}
using (var readTxn = env.BeginTransaction())
{
var alice = db.Get(readTxn, "user:1");
Console.WriteLine(alice); // {"name":"Alice","role":"admin"}
}
using (var txn = env.BeginTransaction())
{
var deleted = db.Delete(txn, "user:1");
Console.WriteLine(deleted); // True
txn.Commit();
}
using (var readTxn = env.BeginTransaction())
{
var alice = db.Get(readTxn, "user:1");
Console.WriteLine(alice); // null
}
LINQ-запросы (Find)
using (var txn = env.BeginTransaction())
{
var allValues = db.Find(txn).Select(e => Encoding.UTF8.GetString(e.Value));
var userNames = db.Find(txn, Encoding.UTF8.GetString).Where(n => n.StartsWith("A"));
var entries = db.Find(txn, Encoding.UTF8.GetString, Encoding.UTF8.GetString);
var rawValues = db.FindValues(txn);
var typed = db.Find(txn, Encoding.UTF8.GetString).First();
txn.Commit();
}
Удаление элементов
using var env = MdbxEnvironment.Open("data.mdbx", maxDatabases: 4);
using var db = env.OpenDatabaseEx("users");
using (var txn = env.BeginTransaction())
{
db.Put(txn, "user:1", "Alice");
db.Put(txn, "user:2", "Bob");
db.Put(txn, "user:3", "Charlie");
using (var cursor = txn.OpenCursor(db))
{
cursor.GetFirst(out var key, out var value);
cursor.Delete();
}
cursor.GetFirst(out var firstKey, out _);
Console.WriteLine(firstKey); // user:2
txn.Commit();
}
MdbxEnvironment.Delete("data.mdbx");
Удаление таблиц (DBI)
using var env = MdbxEnvironment.Open("data.mdbx", maxDatabases: 4);
using var usersDb = env.OpenDatabaseEx("users");
using var logsDb = env.OpenDatabaseEx("logs");
using (var txn = env.BeginTransaction())
{
logsDb.DropDatabase(logsDb.Dbi, deleteData: true);
txn.Commit();
}
Курсоры и итерация
using (var txn = env.BeginTransaction())
using (var cursor = txn.OpenCursor(db))
{
cursor.GetFirst(out var key, out var value);
while (!cursor.IsEof())
{
Console.WriteLine($"{key} = {value}");
cursor.GetNext(out key, out value);
}
cursor.GetLast(out var lastKey);
var current = cursor.GetCurrent(out var currentKey);
nuint count = cursor.Count();
bool onFirst = cursor.IsOnFirst();
bool onLast = cursor.IsOnLast();
cursor.GetNextDup(out var dupKey);
cursor.Renew(txn);
cursor.Put("key", "value");
}
Дубликаты (Dupsort)
using var env = MdbxEnvironment.Open("dup.mdbx", maxDatabases: 2);
using var db = env.OpenDatabaseEx("dup_table", MdbxDbFlags.MDBX_DUPSORT | MdbxDbFlags.MDBX_CREATE);
using (var txn = env.BeginTransaction())
{
db.Put(txn, "fruit", "apple");
db.Put(txn, "fruit", "banana");
db.Put(txn, "fruit", "cherry");
txn.Commit();
}
using (var readTxn = env.BeginTransaction())
using (var cursor = readTxn.OpenCursor(db))
{
cursor.GetFirst(out var key);
cursor.GetNextDup(out var nextVal);
}
Несколько таблиц (DBI)
using var usersDb = env.OpenDatabaseEx("users");
using var logsDb = env.OpenDatabaseEx("logs");
using var defaultDb = env.OpenDatabaseEx();
using (var txn = env.BeginTransaction())
{
usersDb.Put(txn, "u:1", "Alice");
logsDb.Put(txn, "l:1", "created");
defaultDb.Put(txn, "default_key", "default_value");
var u1 = usersDb.Get(txn, "u:1");
var l1 = logsDb.Get(txn, "l:1");
txn.Commit();
}
Флаги и продвинутые сценарии
using var env = MdbxEnvironment.Open("cache.mdbx", maxDatabases: 2);
env.SetOption(MdbxOption.MDBX_OPT_MAX_DB, 4);
env.SetGeometry(sizeLower: -1, sizeNow: -1, sizeUpper: -1);
var stat = env.GetStat();
var info = env.GetInfo();
env.Defrag(progressCallback: (result) =>
{
Console.WriteLine($"Pages compacted: {result->pages}");
return 0;
});
using var db = env.OpenDatabaseEx("flags_demo");
using var txn = env.BeginTransaction();
db.Put(txn, "counter", Encoding.UTF8.GetBytes("1"));
db.Put(txn, "counter", Encoding.UTF8.GetBytes("2"), MdbxPutFlags.MDBX_CURRENT);
txn.Commit();
Обнаружение проблем с медленными читателями (HSR)
env.SetHsr((envPtr, txnPtr, laggard, readers, total, dead, retryNum) =>
{
Console.WriteLine($"Slow reader detected: txn={txnPtr->txn_id}, lag={laggard}");
return 0;
});
Полный список низкоуровневых привязок (LIBMDBX_API)
Каждая функция libmdbx имеет соответствующее P/Invoke-объявление в пространстве имен MDBX.Native.Bindings.*.
Окружение (Env)
mdbx_env_create— создать окружениеmdbx_env_open/mdbx_env_openW— открыть окружение (ANSI/Unicode)mdbx_env_close/mdbx_env_close_ex— закрыть окружениеmdbx_env_delete/mdbx_env_deleteW— удалить окружениеmdbx_env_copy/mdbx_env_copyW/mdbx_env_copy2fd— копирование окруженияmdbx_env_open_for_recovery/mdbx_env_open_for_recoveryW— открыть для восстановленияmdbx_env_turn_for_recovery— переключить мета-страницу для восстановленияmdbx_env_resurrect_after_fork— восстановить после forkmdbx_env_warmup— прогрев (предзагрузка страниц в память)mdbx_env_defrag— дефрагментацияmdbx_env_get_fd— получить файловый дескрипторmdbx_env_get_path/mdbx_env_get_pathW— получить путьmdbx_env_get_option/mdbx_env_set_option— получить/установить параметрmdbx_env_get_flags/mdbx_env_set_flags— получить/установить флагиmdbx_env_set_geometry— установить геометрию (размеры файлов)mdbx_env_get_hsr/mdbx_env_set_hsr— Handle-Slow-Readers колбэкmdbx_env_get_userctx/mdbx_env_set_userctx— пользовательский контекстmdbx_env_get_maxkeysize/mdbx_env_get_maxkeysize_ex— максимальный размер ключаmdbx_env_get_maxvalsize_ex— максимальный размер значенияmdbx_env_get_pairsize4page_max— максимальный размер пары для страницыmdbx_env_get_valsize4page_max— максимальный размер значения для страницыmdbx_env_info_ex— информация об окруженииmdbx_env_stat_ex— статистика окруженияmdbx_env_sync_ex— синхронизацияmdbx_preopen_snapinfo/mdbx_preopen_snapinfoW— снапшот информацииmdbx_txn_copy2pathname/mdbx_txn_copy2pathnameW/mdbx_txn_copy2fd— копирование транзакции
Транзакции (Txn)
mdbx_txn_begin_ex— начать транзакциюmdbx_txn_commit_ex— зафиксировать транзакциюmdbx_txn_commit_embark_read— зафиксировать для embark readmdbx_txn_rollback— откатить транзакциюmdbx_txn_abort_ex— отменить транзакциюmdbx_txn_amend— превратить read-транзакцию в writemdbx_txn_clone— клонировать транзакциюmdbx_txn_reset— сбросить транзакцию (освободить ресурсы)mdbx_txn_renew— обновить (renew) транзакциюmdbx_txn_refresh— обновить (refresh) транзакциюmdbx_txn_break— прервать транзакциюmdbx_txn_park/mdbx_txn_unpark— парковка/распарковка транзакцииmdbx_txn_info— информация о транзакцииmdbx_txn_env— получить окружение транзакцииmdbx_txn_flags— получить флаги транзакцииmdbx_txn_id— получить ID транзакцииmdbx_txn_checkpoint— checkpoint транзакцииmdbx_txn_get_userctx/mdbx_txn_set_userctx— пользовательский контекстmdbx_txn_release_all_cursors_ex— освободить все курсорыmdbx_txn_straggler— информация о «стragler» транзакцииmdbx_txn_lock/mdbx_txn_unlock— блокировка окружения для транзакции
Таблицы (DBI)
mdbx_dbi_open/mdbx_dbi_open2— открыть таблицу по имени/значениюmdbx_dbi_open_ex/mdbx_dbi_open_ex2— открыть с кастомными функциями сравненияmdbx_dbi_close— закрыть таблицуmdbx_dbi_rename/mdbx_dbi_rename2— переименовать таблицуmdbx_dbi_stat— статистика таблицыmdbx_dbi_flags_ex— флаги и состояние таблицыmdbx_dbi_dupsort_depthmask— маска глубины дубликатовmdbx_dbi_sequence— последовательность таблицыmdbx_drop— удалить таблицу
CRUD операции
mdbx_get/mdbx_get_ex— получить значениеmdbx_put— записать значениеmdbx_replace/mdbx_replace_ex— заменить значениеmdbx_del— удалить элементmdbx_get_equal_or_great— получить значение или ближайший больший ключ
Курсоры (Cursor)
mdbx_cursor_create— создать курсорmdbx_cursor_open— открыть курсор для таблицыmdbx_cursor_close/mdbx_cursor_close2— закрыть курсорmdbx_cursor_bind/mdbx_cursor_unbind— привязать/отвязать от таблицыmdbx_cursor_renew— обновить для новой транзакцииmdbx_cursor_reset— сбросить курсорmdbx_cursor_copy— скопировать курсорmdbx_cursor_compare— сравнить два курсораmdbx_cursor_get/mdbx_cursor_get_batch— получить элемент/пакетmdbx_cursor_put— записать через курсорmdbx_cursor_del— удалить через курсорmdbx_cursor_delete_range— удалить диапазонmdbx_cursor_count/mdbx_cursor_count_ex— количество элементовmdbx_cursor_distance— расстояние между курсорамиmdbx_cursor_scroll— прокруткаmdbx_cursor_distribute— распределение элементов между курсорамиmdbx_cursor_bunch_delete— пакетное удалениеmdbx_cursor_eof— достигнут ли конец таблицыmdbx_cursor_on_first/mdbx_cursor_on_last— позиция на первом/последнемmdbx_cursor_on_first_dup/mdbx_cursor_on_last_dup— позиция на первом/последнем дубликатеmdbx_cursor_txn/mdbx_cursor_dbi— получить транзакцию/DBI курсораmdbx_cursor_get_userctx/mdbx_cursor_set_userctx— пользовательский контекстmdbx_cursor_ignord— игнорировать дубликатыmdbx_cursor_scan/mdbx_cursor_scan_from— сканирование с предикатом
Настройки (Settings)
mdbx_env_set_option/mdbx_env_get_option— параметры окруженияmdbx_env_set_flags/mdbx_env_get_flags— флаги окруженияmdbx_env_set_geometry— геометрия окруженияmdbx_env_set_hsr/mdbx_env_get_hsr— HSR колбэкmdbx_env_set_userctx/mdbx_env_get_userctx— пользовательский контекстmdbx_default_pagesize— размер страницы по умолчаниюmdbx_get_sysraminfo— информация о системной RAMmdbx_is_readahead_reasonable— проверка readaheadmdbx_limits_dbsize_min/mdbx_limits_dbsize_max— лимиты размера БДmdbx_limits_keysize_min/mdbx_limits_keysize_max— лимиты размера ключаmdbx_limits_valsize_min/mdbx_limits_valsize_max— лимиты размера значенияmdbx_limits_pairsize4page_max— максимальный размер пары для страницыmdbx_limits_valsize4page_max— максимальный размер значения для страницыmdbx_limits_txnsize_max— максимальный размер транзакцииmdbx_ratio2digits/mdbx_ratio2percents— форматирование соотношений
Обработка ошибок (Error Handling)
mdbx_strerror/mdbx_strerror_r— описание ошибкиmdbx_liberr2str— описание ошибки (только libmdbx)mdbx_strerror_ANSI2OEM/mdbx_strerror_r_ANSI2OEM— ANSI/OEM версииmdbx_assert_fail— аварийное завершение при assertmdbx_set_panic— функция обработки паники
Дополнительные возможности (Extra)
mdbx_cmp/mdbx_dcmp— сравнение ключей/значенийmdbx_get_keycmp/mdbx_get_datacmp— получить функцию сравненияmdbx_key_from_jsonInteger/mdbx_jsonInteger_from_key— JSON integer ключиmdbx_key_from_double/mdbx_double_from_key— double ключиmdbx_key_from_float/mdbx_float_from_key— float ключиmdbx_key_from_ptrdouble/mdbx_key_from_ptrfloat— указатели на float/doublemdbx_int32_from_key/mdbx_int64_from_key— int32/int64 из ключаmdbx_enumerate_tables— перечисление таблицmdbx_reader_list/mdbx_reader_check— работа с читателямиmdbx_thread_register/mdbx_thread_unregister— регистрация потоковmdbx_txn_lock/mdbx_txn_unlock— блокировка окруженияmdbx_estimate_distance/mdbx_estimate_move/mdbx_estimate_range— оценкиmdbx_cache_get/mdbx_cache_get_SingleThreaded— кэшированиеmdbx_env_chk/mdbx_env_chk_encount_problem— проверка целостностиmdbx_is_dirty— проверка «грязных» страницmdbx_gc_info— информация о GC и использовании страницmdbx_dump_val— форматирование значения в строку
Статистика (Stat)
mdbx_env_stat_ex— статистика окруженияmdbx_env_info_ex— информация об окруженииmdbx_env_sync_ex— синхронизация окружения
Отладка (Debug)
mdbx_setup_debug/mdbx_setup_debug_nofmt— настройка логированияmdbx_canary_put/mdbx_canary_get— структура-маяк
Покрытие тестами
Проект содержит модульные тесты для большинства низкоуровневых привязок и высокоуровневых сценариев использования.
✅ Хорошее покрытие
| Модуль | Что покрыто |
|---|---|
| Cursor (Native) | Open/Close/Create/Bind/Copy, Get/Put/Del, навигация (First/Last/Next/Prev), Count/CountEx, Distance, DeleteRange, DupSort (OnFirstDup/OnLastDup, Ignord), Batch, Userctx, Renew/Reset/Unbind, Compare, Txn/Dbi accessors |
| CRUD (Native) | Put (MDBX_UPSERT, MDBX_CURRENT), Get, GetEx, Replace, ReplaceEx, Del, GetEqualOrGreat |
| DBI (Native) | Open/Open2/OpenEx/OpenEx2, Close, Drop, Rename/Rename2, Stat, FlagsEx, DupsortDepthmask, Sequence |
| Settings | SetOption/GetOption, SetFlags/GetFlags, GetMaxkeysize/Ex, GetMaxvalsize/Ex, GetPath/GetPathW, GetFd, SetUserctx/GetUserctx, DefaultPagesize, SetGeometry, SetHsr/GetHsr, все Limits* функции, Ratio2Digits, Ratio2Percents |
| Extra (Native) | Cmp, GetKeycmp, GetDatacmp, KeyFrom* конвертации, Float/Double/Int32/Int64/JsonInteger извлечение, IsDirty, GcInfo, CacheGet/SingleThreaded, ReaderList/Check, ThreadRegister/Unregister, EstimateDistance/EstimateMove, EnumerateTables, TxnLock/Unlock, EnvChk |
| Txn (Native) | BeginEx, CommitEx, Rollback, AbortEx, Clone, Reset, Renew, Refresh, Info, Id, Env, Flags, SetUserctx/GetUserctx, Checkpoint, Break, Park/Unpark, ReleaseAllCursorsEx, Straggler |
| Env (Native) | Create, Open/OpenW, Close/CloseEx, Delete/DeleteW, Copy/CopyW/Copy2Fd, OpenForRecovery/OpenForRecoveryW, TurnForRecovery, Warmup, Defrag, PreopenSnapinfo/PreopenSnapinfoW |
| ErrorHandling | Strerror, StrerrorR, Liberr2Str, ANSI2OEM версии, SetPanic |
⚠️ Ограниченное покрытие
| Модуль | Что не покрыто |
|---|---|
| Query (High-level) | Только 4 базовых теста; нет тестов для range queries, First/Single/Any/Count, cursor navigation внутри query, сложных предикатов |
| Stat | Только 3 функции (EnvStatEx, EnvInfoEx, EnvSyncEx); нет DbiStat в isolation, нет non-Ex variants |
| CRUD (High-level) | Нет тестов для dupsort/multiple-value; нет тестов флагов MDBX_APPEND, MDBX_APPENDDUP, MDBX_NOOVERWRITE, MDBX_MULTIPLE; нет concurrency-тестов |
| Debug | Только 4 функции (SetupDebug, SetupDebugNofmt, CanaryPut/Get, DumpVal) |
| Examples | Служат integration-level smoke-тестами, но не покрывают edge-cases |
❌ Не покрыто
- Многопоточность — нет тестов для concurrent readers/writers, deadlock detection
- Transaction Nesting — нет тестов для вложенных транзакций
- Dupsort/Многозначение (High-level) — нет high-level тестов для дубликатов, append, multi-value
- Восстановление после повреждений —
OpenForRecoverageтестируется, но нет actual corruption/recovery validation - HSR (Handle-Slow-Readers) — только SetHsr/GetHsr с null; нет реальных HSR behavior тестов
- Checkpoint — только вызов с null; нет LSN/TWN validation
- Large values / overflow pages — нет тестов для значений превышающих размер страницы
Документация
Сгенерировать документацию:
docfx docfx.json --serve
Сборка
dotnet build libmdbx-dotnet.sln
Тестирование
dotnet test libmdbx-dotnet.sln