Skip to content

File usage.md

File List > usage.md

Go to the documentation of this file

\page usage Usage
\section getting Building & Embedding
<!-- section-begin usage -->

Since December 2025 _libmdbx_ is available only in an amalgamated source code form like [SQLite](https://www.sqlite.org/amalgamation.html), without additional dependencies and internal resources needed only for development of _libmdbx_ itself. Package support for common Linux distributions is planned in the future, after the `1.0` release.

The source code is available on [SourceCraft](https://sourcecraft.dev/dqdkfa/libmdbx) and mirror on [GitHub](https://github.com/Mithril-mine/libmdbx).
Please use the `stable` branch or the latest release for production environments through staging, and the `master` branch for development of derivative projects.

## Building and Testing

[Source code](https://en.wikipedia.org/wiki/Source_code) provides build through the use of [CMake](https://cmake.org/) or [GNU Make](https://www.gnu.org/software/make/) with [bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)).

All build ways are completely traditional and have minimal prerequisites like `build-essential`, i.e. the non-obsolete C/C++ compiler and a [SDK](https://en.wikipedia.org/wiki/Software_development_kit) for the target platform. Obviously you need building tools itself, i.e. `git`, `cmake` or GNU `make` with `bash`. For your convenience, `make help` and `make options` are also available for listing existing targets and build options respectively.

So just use CMake or GNU Make in your habitual manner, and feel free to file an issue in case something is unexpected or breaks down.

### Testing
Amalgamated source code does not contain most of the tests and other internal components for several reasons. You can find explanations of the reasons in the comments to the presentation of [_libmdbx_ roadmap](https://libmdbx.dqdkfa.ru/release/libmdbx-roadmap-HNY2026-english.pdf) on the eve of 2026. However, an extended example of using the C++ API will be added soon, which can also be used as a simple smoke-test.

### Common important details

#### Build reproducibility
By default _libmdbx_ track build time via `MDBX_BUILD_TIMESTAMP` build option and macro. So for a [reproducible builds](https://en.wikipedia.org/wiki/Reproducible_builds) you should predefine/override it to known fixed string value. For instance:

 - for reproducible build with make: `make MDBX_BUILD_TIMESTAMP=unknown ` ...
 - or during configure by CMake: `cmake -DMDBX_BUILD_TIMESTAMP:STRING=unknown ` ...

Of course, in addition to this, your toolchain must ensure the reproducibility of builds. For more information please refer to [reproducible-builds.org](https://reproducible-builds.org/).

#### Containers
There are no special traits nor quirks if you use _libmdbx_ ONLY inside the single container. But in a cross-container(s) or with a host-container(s) interoperability cases the three major things MUST be guaranteed:

1. Coherence of memory mapping content and unified page cache inside OS kernel for host and all container(s) operated with a DB. Basically this means there must be only a single physical copy of each memory mapped DB' page in the system memory.

2. Uniqueness of [PID](https://en.wikipedia.org/wiki/Process_identifier) values and/or a common space for them:
    - for POSIX systems: PID uniqueness for all processes operated with a DB. I.e. the `--pid=host` is required to run DB-aware processes inside Docker, or, without host interaction, a `--pid=container:<name|id>` with the same name/id.
    - for non-POSIX (i.e. Windows) systems: inter-visibility of processes handles. I.e. the `OpenProcess(SYNCHRONIZE, ..., PID)` must return reasonable error, including `ERROR_ACCESS_DENIED`, but not the `ERROR_INVALID_PARAMETER` as for an invalid/non-existent PID.

3. The versions/builds of _libmdbx_ and `libc`/`pthreads` (`glibc`, `musl`, etc) must be compatible.
   - Basically, the `options:` string in the output of `mdbx_chk -V` must be the same for host and container(s). See `MDBX_LOCKING`, `MDBX_USE_OFDLOCKS` and other build options for details.
   - Avoid using different versions of `libc`, especially mixing different implementations, i.e. `glibc` with `musl`, etc. Prefer to use the same LTS version, or switch to full virtualization/isolation if in doubt.

#### DSO/DLL unloading and destructors of Thread-Local-Storage objects
When building _libmdbx_ as a shared library or using static _libmdbx_ as a part of another dynamic library, it is advisable to make sure that your system ensures the correctness of calling the destructors of Thread-Local-Storage objects when unloading dynamic libraries.

If this is not the case, then unloading a dynamic-link library with _libmdbx_ code inside, can result in either a resource leak or a crash due to calling destructors from an already unloaded DSO/DLL object. The problem can only manifest in a multithreaded application, which makes the unloading of shared dynamic libraries with _libmdbx_ code inside, after using _libmdbx_. It is known that TLS-destructors are properly maintained in the following cases:

- On all modern versions of Windows (Windows 7 and later).

- On systems with the [`__cxa_thread_atexit_impl()`](https://sourceware.org/glibc/wiki/Destructor%20support%20for%20thread_local%20variables) function in the standard C library, including systems with GNU libc version 2.18 and later.

- On systems with libpthread/ntpl from GNU libc with bug fixes [#21031](https://sourceware.org/bugzilla/show_bug.cgi?id=21031) and [#21032](https://sourceware.org/bugzilla/show_bug.cgi?id=21032), or where there are no similar bugs in the pthreads implementation.

### Linux and other platforms with GNU Make
To build the library it is enough to execute `make all` in the directory of source code, and `make check` to execute the basic tests.

If the `make` installed on the system is not GNU Make, there will be a lot of errors from make when trying to build. In this case, perhaps you should use `gmake` instead of `make`, or even `gnu-make`, etc.

### FreeBSD and related platforms
As a rule on BSD and its derivatives the default is to use Berkeley Make and [Bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)) is not installed.

So you need to install the required components: GNU Make, Bash, C and C++ compilers compatible with GCC or CLANG. After that, to build the library, it is enough to execute `gmake all` (or `make all`) in the directory with source code, and `gmake check` (or `make check`) to run the basic tests.

### Windows
To build _libmdbx_ on Windows the _original_ CMake and [Microsoft Visual Studio 2019 or 2022](https://en.wikipedia.org/wiki/Microsoft_Visual_Studio) are recommended. Please use the recent versions of CMake, Visual Studio and Windows SDK to avoid troubles with C11 support and `alignas()` feature.

To build with MinGW the 10.2 or recent version coupled with a modern CMake are required. So it is recommended to use [chocolatey](https://chocolatey.org/) to install and/or update the ones.

Other ways to build are potentially possible but are not supported and will not be. The `CMakeLists.txt` or `GNUMakefile` scripts will probably need to be modified accordingly. Using other methods do not forget to add the `ntdll.lib` to linking.

It should be noted that in _libmdbx_ there were efforts to avoid runtime dependencies from CRT and other MSVC libraries. For this is enough to pass the `-DMDBX_WITHOUT_MSVC_CRT:BOOL=ON` option during configure by CMake.

### Windows Subsystem for Linux
_libmdbx_ could be used in [WSL2](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux#WSL_2) but NOT in [WSL1](https://en.wikipedia.org/wiki/Windows_Subsystem_for_Linux#WSL_1) environment. This is a consequence of the fundamental shortcomings of _WSL1_ and cannot be fixed. To avoid data loss, _libmdbx_ returns the `ENOLCK` (37, "No record locks available") error when opening the database in a _WSL1_ environment.

### MacOS
Current [native build tools](https://en.wikipedia.org/wiki/Xcode) for MacOS include GNU Make, CLANG and an outdated version of Bash. However, the build script uses GNU-kind of `sed` and `tar`. So the easiest way to install all prerequisites is to use [Homebrew](https://brew.sh/), just by `brew install bash make cmake ninja gnu-sed gnu-tar --with-default-names`.

Next, to build the library, it is enough to run `make all` in the directory with source code, and run `make check` to execute the base tests. If something goes wrong, it is recommended to install [Homebrew](https://brew.sh/) and try again.

### Harmony OS
Please use CMake with the ["toolchain file"](https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html) provided by HarmonyOS SDK.

### Android
Please use CMake to build _libmdbx_ for Android. Please refer to the [official guide](https://developer.android.com/studio/projects/add-native-code).

### iOS
To build _libmdbx_ for iOS, please use CMake with the ["toolchain file"](https://cmake.org/cmake/help/latest/variable/CMAKE_TOOLCHAIN_FILE.html) from the [ios-cmake](https://github.com/leetal/ios-cmake) project.

<!-- section-end -->
Getting started {#starting}
===============

> This section is based on Bert Hubert's intro "LMDB Semantics", with
> edits reflecting the improvements and enhancements were made in MDBX.
> See Bert Hubert's [original](https://github.com/ahupowerdns/ahutils/blob/master/lmdb-semantics.md).

Everything starts with an environment, created by \ref mdbx_env_create().
Once created, this environment must also be opened with \ref mdbx_env_open(),
and after use be closed by \ref mdbx_env_close(). At that a non-zero value
of the last argument "mode" supposes MDBX will create database and directory
if ones does not exist. In this case the non-zero "mode" argument specifies
the file mode bits be applied when a new files are created by `open()` function.

Within that directory, a lock file (aka LCK-file) and a storage file (aka
DXB-file) will be generated. If you don't want to use a directory, you can
pass the \ref MDBX_NOSUBDIR option, in which case the path you provided is used
directly as the DXB-file, and another file with a "-lck" suffix added
will be used for the LCK-file.

Once the environment is open, a transaction can be created within it using
\ref mdbx_txn_begin(). Transactions may be read-write or read-only, and read-write
transactions may be nested. A transaction must only be used by one thread at
a time. Transactions are always required, even for read-only access. The
transaction provides a consistent view of the data.

Once a transaction has been created, a database (i.e. key-value space inside
the environment) can be opened within it using \ref mdbx_dbi_open(). If only one
database will ever be used in the environment, a `NULL` can be passed as the
database name. For named databases, the \ref MDBX_CREATE flag must be used to
create the database if it doesn't already exist. Also, \ref mdbx_env_set_maxdbs()
must be called after \ref mdbx_env_create() and before \ref mdbx_env_open() to set
the maximum number of named databases you want to support.

\note A single transaction can open multiple databases. Generally databases
should only be opened once, by the first transaction in the process.

Within a transaction, \ref mdbx_get() and \ref mdbx_put() can store single key-value
pairs if that is all you need to do (but see \ref Cursors below if you want to do
more).

A key-value pair is expressed as two \ref MDBX_val structures. This struct that is
exactly similar to POSIX's `struct iovec` and has two fields, `iov_len` and
`iov_base`. The data is a `void` pointer to an array of `iov_len` bytes.
\note The notable difference between MDBX and LMDB is that MDBX support zero
length keys.

Because MDBX is very efficient (and usually zero-copy), the data returned in
an \ref MDBX_val structure may be memory-mapped straight from disk. In other words
look but do not touch (or `free()` for that matter). Once a transaction is
closed, the values can no longer be used, so make a copy if you need to keep
them after that.

## Cursors {#Cursors}
To do more powerful things, we must use a cursor.

Within the transaction, a cursor can be created with \ref mdbx_cursor_open().
With this cursor we can store/retrieve/delete (multiple) values using
\ref mdbx_cursor_get(), \ref mdbx_cursor_put() and \ref mdbx_cursor_del().

The \ref mdbx_cursor_get() positions itself depending on the cursor operation
requested, and for some operations, on the supplied key. For example, to list
all key-value pairs in a database, use operation \ref MDBX_FIRST for the first
call to \ref mdbx_cursor_get(), and \ref MDBX_NEXT on subsequent calls, until
the end is hit.

To retrieve all keys starting from a specified key value, use \ref MDBX_SET. For
more cursor operations, see the \ref c_api reference.

When using \ref mdbx_cursor_put(), either the function will position the cursor
for you based on the key, or you can use operation \ref MDBX_CURRENT to use the
current position of the cursor. \note Note that key must then match the current
position's key.


## Summarizing the opening

So we have a cursor in a transaction which opened a database in an
environment which is opened from a filesystem after it was separately
created.

Or, we create an environment, open it from a filesystem, create a transaction
within it, open a database within that transaction, and create a cursor
within all of the above.

Got it?


## Threads and processes

Do not have open an database twice in the same process at the same time, MDBX
will track and prevent this. Instead, share the MDBX environment that has
opened the file across all threads. The reason for this is:
 - When the "Open file description" locks (aka OFD-locks) are not available,
   MDBX uses POSIX locks on files, and these locks have issues if one process
   opens a file multiple times.
 - If a single process opens the same environment multiple times, closing it
   once will remove all the locks held on it, and the other instances will be
   vulnerable to corruption from other processes.
 + For compatibility with LMDB which allows multi-opening, MDBX can be
   configured at runtime by \ref mdbx_setup_debug() with \ref MDBX_DBG_LEGACY_MULTIOPEN option
   prior to calling other MDBX functions. In this way MDBX will track
   databases opening, detect multi-opening cases and then recover POSIX file
   locks as necessary. However, lock recovery can cause unexpected pauses,
   such as when another process opened the database in exclusive mode before
   the lock was restored - we have to wait until such a process releases the
   database, and so on.

Do not use opened MDBX environment(s) after `fork()` in a child process(es),
MDBX will check and prevent this at critical points. Nonetheless, If such scenarios
are required, be sure to use the \ref mdbx_env_resurrect_after_fork().

Do not start more than one transaction for a one thread. If you think
about this, it's really strange to do something with two data snapshots
at once, which may be different. MDBX checks and preventing this by
returning corresponding error code (\ref MDBX_TXN_OVERLAPPING,
\ref MDBX_BAD_RSLOT, \ref MDBX_BUSY) unless you using
\ref MDBX_NOSTICKYTHREADS option on the environment. Nonetheless,
with the \ref MDBX_NOSTICKYTHREADS option, you must know exactly what
you are doing, otherwise you will get deadlocks or reading an alien
data.

Also note that a transaction is tied to one thread by default using
Thread Local Storage. If you want to pass transactions across threads,
you can use the \ref MDBX_NOSTICKYTHREADS option on the environment.
Nevertheless, a write transaction must be committed or aborted in the
same thread which it was started. MDBX checks this in a reasonable
manner and return the \ref MDBX_THREAD_MISMATCH error in rules
violation.


## Transactions, rollbacks etc

To actually get anything done, a transaction must be committed using
\ref mdbx_txn_commit(). Alternatively, all of a transaction's operations
can be discarded using \ref mdbx_txn_abort().

\attention An important difference between MDBX and LMDB is that MDBX required
that any opened cursors can be reused and must be freed explicitly, regardless
ones was opened in a read-only or write transaction. The REASON for this is
eliminates ambiguity which helps to avoid errors such as: use-after-free,
double-free, i.e. memory corruption and segfaults.

For read-only transactions, obviously there is nothing to commit to storage.
\attention An another notable difference between MDBX and LMDB is that MDBX make
handles opened for existing databases immediately available for other
transactions, regardless this transaction will be aborted or reset. The
REASON for this is to avoiding the requirement for multiple opening a same
handles in concurrent read transactions, and tracking of such open but hidden
handles until the completion of read transactions which opened them.

In addition, as long as a transaction is open, a consistent view of the
database is kept alive, which requires storage. A read-only transaction that
no longer requires this consistent view should be terminated (committed or
aborted) when the view is no longer needed (but see below for an
optimization).

There can be multiple simultaneously active read-only transactions but only
one that can write. Once a single read-write transaction is opened, all
further attempts to begin one will block until the first one is committed or
aborted. This has no effect on read-only transactions, however, and they may
continue to be opened at any time.


## Duplicate keys aka Multi-values

\ref mdbx_get() and \ref mdbx_put() respectively have no and only some support or
multiple key-value pairs with identical keys. If there are multiple values
for a key, \ref mdbx_get() will only return the first value.

When multiple values for one key are required, pass the \ref MDBX_DUPSORT flag to
\ref mdbx_dbi_open(). In an \ref MDBX_DUPSORT database, by default \ref mdbx_put() will
not replace the value for a key if the key existed already. Instead it will add
the new value to the key. In addition, \ref mdbx_del() will pay attention to the
value field too, allowing for specific values of a key to be deleted.

Finally, additional cursor operations become available for traversing through
and retrieving duplicate values.


## Some optimization

If you frequently begin and abort read-only transactions, as an optimization,
it is possible to only reset and renew a transaction.

\ref mdbx_txn_reset() releases any old copies of data kept around for a read-only
transaction. To reuse this reset transaction, call \ref mdbx_txn_renew() on it.
Any cursors in this transaction can also be renewed using \ref mdbx_cursor_renew()
or freed by \ref mdbx_cursor_close().

To permanently free a transaction, reset or not, use \ref mdbx_txn_abort().


## Cleaning up

Any created cursors must be closed using \ref mdbx_cursor_close(). It is advisable
to repeat:
\note An important difference between MDBX and LMDB is that MDBX required that
any opened cursors can be reused and must be freed explicitly, regardless
ones was opened in a read-only or write transaction. The REASON for this is
eliminates ambiguity which helps to avoid errors such as: use-after-free,
double-free, i.e. memory corruption and segfaults.

It is very rarely necessary to close a database handle, and in general they
should just be left open. When you close a handle, it immediately becomes
unavailable for all transactions in the environment. Therefore, you should
avoid closing the handle while at least one transaction is using it.


## Now read up on the full API!

The full \ref c_api documentation lists further details below, like how to:

- Configure database size and automatic size management: \ref mdbx_env_set_geometry().
- Drop and clean a database: \ref mdbx_drop().
- Detect and report errors: \ref c_err.
- Optimize (bulk) loading speed: \ref MDBX_MULTIPLE, \ref MDBX_APPEND.
- Reduce (temporarily) robustness to gain even more speed: \ref sync_modes.
- Gather statistics about the database: \ref c_statinfo.
- Estimate size of range query result: \ref c_rqest.
- Double performance by LIFO reclaiming on storages with write-back: \ref MDBX_LIFORECLAIM.
- Use sequences and canary markers: \ref mdbx_dbi_sequence(), \ref MDBX_canary.
- Use Handle-Slow-Readers callback to resolve a database full/overflow issues
  due to long-lived read transactions: \ref mdbx_env_set_hsr().
- Use exclusive mode: \ref MDBX_EXCLUSIVE.
- Define custom sort orders (but this is recommended to be avoided).
<!-- section-begin bindings -->

Bindings {#bindings}
========

The full list of all bindings (several dozen) is available in the [Bindings and Projects](https://libmdbx.dqdkfa.ru/#sec-projects) section of the libmdbx homesite; here are only a few of the highest demand.

| Runtime |  Repo  | Author |
| ------- | ------ | ------ |
| Rust    | [libmdbx-rs](https://github.com/vorot93/libmdbx-rs)   | [Artem Vorotnikov](https://github.com/vorot93) |
| Go      | [mdbx-go](https://github.com/torquem-ch/mdbx-go)      | [Alex Sharov](https://github.com/AskAlexSharov) |
| .NET    | [libmdbx-dotnet](https://public.git.amsoft.spb.ru/libmdbx/libmdbx-dotnet) | [Anton Maisak](mailto:anton@maisak.ru) |
| CPython | [PyPi/clibmdbx](https://pypi.org/project/clibmdbx/)   | [`@jyj117`](https://github.com/jyj117) |
| Python  | [PyPi/libmdbx](https://pypi.org/project/libmdbx/)     | [Lazymio](https://github.com/wtdcode) |
| NodeJS  | [mdbxmou](https://github.com/ikonopistsev/mdbxmou)    | [Igor Ikonopistsev](https://github.com/ikonopistsev) |
| Zig     | [mdbx-zig](https://github.com/theseyan/lmdbx-zig)     | [Sayan J. Das](https://github.com/theseyan) |

<!-- section-end -->