2019-08-11 05:23:18 +08:00
## The [repository was moved out from Github](https://abf.io/erthink/libmdbx) due to illegal discriminatory restrictions for Russian Crimea and for sovereign crimeans.
<!-- Required extensions: pymdownx.betterem, pymdownx.tilde, pymdownx.emoji, pymdownx.tasklist, pymdownx.superfences -->
2019-07-28 03:13:28 +08:00
---
2017-09-17 20:30:41 +08:00
libmdbx
2017-01-09 02:48:00 +08:00
======================================
2018-01-18 03:19:59 +08:00
**Revised and extended descendant of [Symas LMDB ](https://symas.com/lmdb/ ).**
2017-01-09 02:48:00 +08:00
2018-01-18 03:19:59 +08:00
*The Future will be positive.*
2017-07-30 05:58:11 +08:00
[![Build Status ](https://travis-ci.org/leo-yuriev/libmdbx.svg?branch=master )](https://travis-ci.org/leo-yuriev/libmdbx)
[![Build status ](https://ci.appveyor.com/api/projects/status/ue94mlopn50dqiqg/branch/master?svg=true )](https://ci.appveyor.com/project/leo-yuriev/libmdbx/branch/master)
2017-06-12 20:41:36 +08:00
[![Coverity Scan Status ](https://scan.coverity.com/projects/12915/badge.svg )](https://scan.coverity.com/projects/reopen-libmdbx)
2017-01-09 02:48:00 +08:00
2018-05-10 19:05:48 +08:00
## Project Status for now
2017-11-15 23:47:39 +08:00
2018-07-30 07:55:13 +08:00
- The stable versions
([_stable/0.0_](https://github.com/leo-yuriev/libmdbx/tree/stable/0.0)
and
[_stable/0.1_ ](https://github.com/leo-yuriev/libmdbx/tree/stable/0.1 )
branches) of _MDBX_ are frozen, i.e. no new features or API changes, but
only bug fixes.
- The next version
([_devel_](https://github.com/leo-yuriev/libmdbx/tree/devel) branch)
**is under active non-public development** , i.e. current API and set of
features are extreme volatile.
- The immediate goal of development is formation of the stable API and
the stable internal database format, which allows realise all PLANNED
FEATURES:
2018-05-25 05:00:58 +08:00
1. Integrity check by [Merkle tree ](https://en.wikipedia.org/wiki/Merkle_tree );
2. Support for [raw block devices ](https://en.wikipedia.org/wiki/Raw_device );
2018-05-22 17:01:35 +08:00
3. Separate place (HDD) for large data items;
2018-05-25 05:00:58 +08:00
4. Using "[Roaring bitmaps](http://roaringbitmap.org/about/)" inside garbage collector;
2018-05-29 07:04:51 +08:00
5. Non-sequential reclaiming, like PostgreSQL's [Vacuum ](https://www.postgresql.org/docs/9.1/static/sql-vacuum.html );
2018-05-25 05:00:58 +08:00
6. [Asynchronous lazy data flushing ](https://sites.fas.harvard.edu/~cs265/papers/kathuria-2008.pdf ) to disk(s);
2018-05-22 17:01:35 +08:00
7. etc...
2018-05-10 19:05:48 +08:00
2018-10-17 09:42:26 +08:00
Don't miss libmdbx for other runtimes.
| Runtime | GitHub | Author |
| ------------- | ------------- | ------------- |
| JVM | [mdbxjni ](https://github.com/castortech/mdbxjni ) | [Castor Technologies ](https://castortech.com/ ) |
| .NET | [mdbx.NET ](https://github.com/wangjia184/mdbx.NET ) | [Jerry Wang ](https://github.com/wangjia184 ) |
2018-06-19 04:04:49 +08:00
2018-05-10 19:05:48 +08:00
-----
2018-03-26 19:17:35 +08:00
2019-08-11 05:23:18 +08:00
Nowadays MDBX works on Linux and OS'es compliant with POSIX.1-2008, but
also support Windows (since Windows XP) as a complementary platform.
Support for other OS could be implemented on commercial basis. However
such enhancements (i.e. pull requests) could be accepted in mainstream
only when corresponding public and free Continuous Integration service
will be available.
2017-11-15 23:47:39 +08:00
2018-01-18 03:19:59 +08:00
## Contents
- [Overview ](#overview )
- [Comparison with other DBs ](#comparison-with-other-dbs )
2018-05-29 07:04:51 +08:00
- [History & Acknowledgments ](#history )
2018-01-18 03:19:59 +08:00
- [Main features ](#main-features )
2018-07-30 07:55:13 +08:00
- [Improvements over LMDB ](#improvements-over-lmdb )
- [Gotchas ](#gotchas )
2019-07-16 08:16:25 +08:00
- [Problem of long-time reading ](#problem-of-long-time-reading )
- [Durability in asynchronous writing mode ](#durability-in-asynchronous-writing-mode )
2018-01-18 20:47:17 +08:00
- [Performance comparison ](#performance-comparison )
- [Integral performance ](#integral-performance )
2018-01-18 03:19:59 +08:00
- [Read scalability ](#read-scalability )
- [Sync-write mode ](#sync-write-mode )
- [Lazy-write mode ](#lazy-write-mode )
- [Async-write mode ](#async-write-mode )
- [Cost comparison ](#cost-comparison )
2017-02-08 22:52:05 +08:00
2017-07-21 06:59:05 +08:00
2018-01-18 03:19:59 +08:00
## Overview
2018-07-30 07:55:13 +08:00
_libmdbx_ is an embedded lightweight key-value database engine oriented
for performance under Linux and Windows.
2017-07-21 06:59:05 +08:00
2018-07-30 07:55:13 +08:00
_libmdbx_ allows multiple processes to read and update several key-value
tables concurrently, while being
[ACID ](https://en.wikipedia.org/wiki/ACID )-compliant, with minimal
2019-07-16 08:16:25 +08:00
overhead and Olog(N) operation cost.
2017-02-08 22:52:05 +08:00
2019-07-16 08:16:25 +08:00
_libmdbx_ enforce [serializability ](https://en.wikipedia.org/wiki/Serializability ) for writers by single [mutex ](https://en.wikipedia.org/wiki/Mutual_exclusion ) and affords [wait-free ](https://en.wikipedia.org/wiki/Non-blocking_algorithm#Wait-freedom ) for parallel readers without atomic/interlocked operations, while writing and reading transactions do not block each other.
2018-07-30 07:55:13 +08:00
2019-07-16 08:16:25 +08:00
_libmdbx_ can guarantee consistency after crash depending of operation mode.
2018-07-30 07:55:13 +08:00
_libmdbx_ uses [B+Trees ](https://en.wikipedia.org/wiki/B%2B_tree ) and
2019-07-16 08:16:25 +08:00
[Memory-Mapping ](https://en.wikipedia.org/wiki/Memory-mapped_file ), doesn't use
[WAL ](https://en.wikipedia.org/wiki/Write-ahead_logging ) which
might be a caveat for some workloads.
2017-11-15 23:47:39 +08:00
2018-01-18 03:19:59 +08:00
### Comparison with other DBs
2019-07-16 08:16:25 +08:00
For now please refer to [chapter of "BoltDB comparison with other
databases"](https://github.com/coreos/bbolt#comparison-with-other-databases)
which is also (mostly) applicable to MDBX.
2017-02-08 22:52:05 +08:00
2018-01-18 03:19:59 +08:00
### History
2018-07-30 07:55:13 +08:00
The _libmdbx_ design is based on [Lightning Memory-Mapped
Database](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database).
2019-07-16 08:16:25 +08:00
Initial development was going in [ReOpenLDAP ](https://github.com/leo-yuriev/ReOpenLDAP ) project.
About a year later libmdbx was isolated to separate project, which was [presented at Highload++
2018-07-30 07:55:13 +08:00
2015 conference](http://www.highload.ru/2015/abstracts/1831.html).
2019-07-16 08:16:25 +08:00
Since early 2017 _libmdbx_ is used in [Fast Positive Tables ](https://github.com/leo-yuriev/libfpta ),
and development is funded by [Positive Technologies ](https://www.ptsecurity.com ).
2017-01-18 21:40:56 +08:00
2018-05-29 07:04:51 +08:00
#### Acknowledgments
2018-07-30 07:55:13 +08:00
Howard Chu (Symas Corporation) - the author of LMDB, from which
originated the MDBX in 2015.
2017-09-17 20:07:32 +08:00
2018-07-30 07:55:13 +08:00
Martin Hedenfalk < martin @ bzero . se > - the author of `btree.c` code, which
was used for begin development of LMDB.
2017-09-17 20:07:32 +08:00
2018-01-18 03:19:59 +08:00
Main features
2018-01-18 20:47:17 +08:00
=============
2017-01-18 21:40:56 +08:00
2019-07-19 11:17:12 +08:00
_libmdbx_ inherits all features and characteristics from
2018-01-18 03:19:59 +08:00
[LMDB ](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database ):
2017-01-18 21:40:56 +08:00
2019-07-19 11:17:12 +08:00
1. Key-value pairs are stored in ordered map(s), keys are always sorted,
range lookups are supported.
2018-07-30 07:55:13 +08:00
2019-07-19 11:17:12 +08:00
2. Data is [memory-mapped ](https://en.wikipedia.org/wiki/Memory-mapped_file )
into each worker DB process, and could be accessed zero-copy from transactions.
2018-07-30 07:55:13 +08:00
3. Transactions are
2019-07-19 11:17:12 +08:00
[ACID ](https://en.wikipedia.org/wiki/ACID )-compliant, through to
2018-07-30 07:55:13 +08:00
[MVCC ](https://en.wikipedia.org/wiki/Multiversion_concurrency_control )
and [CoW ](https://en.wikipedia.org/wiki/Copy-on-write ). Writes are
strongly serialized and aren't blocked by reads, transactions can't
conflict with each other. Reads are guaranteed to get only commited data
([relaxing serializability](https://en.wikipedia.org/wiki/Serializability#Relaxing_serializability)).
2019-07-19 11:17:12 +08:00
4. Read transactions are
2018-07-30 07:55:13 +08:00
[non-blocking ](https://en.wikipedia.org/wiki/Non-blocking_algorithm ),
2019-07-19 11:17:12 +08:00
don't use [atomic operations ](https://en.wikipedia.org/wiki/Linearizability#High-level_atomic_operations ).
2018-07-30 07:55:13 +08:00
Readers don't block each other and aren't blocked by writers. Read
performance scales linearly with CPU core count.
2019-07-19 11:17:12 +08:00
> Nonetheless, "connect to DB" (starting the first read transaction in a thread) and
> "disconnect from DB" (closing DB or thread termination) requires a lock
> acquisition to register/unregister at the "readers table".
2018-07-30 07:55:13 +08:00
5. Keys with multiple values are stored efficiently without key
2019-07-19 11:17:12 +08:00
duplication, sorted by value, including integers (valuable for
2018-07-30 07:55:13 +08:00
secondary indexes).
2019-07-19 11:17:12 +08:00
6. Efficient operation on short fixed length keys,
including 32/64-bit integer types.
2018-07-30 07:55:13 +08:00
7. [WAF ](https://en.wikipedia.org/wiki/Write_amplification ) (Write
Amplification Factor) и RAF (Read Amplification Factor) are Olog(N).
8. No [WAL ](https://en.wikipedia.org/wiki/Write-ahead_logging ) and
transaction journal. In case of a crash no recovery needed. No need for
regular maintenance. Backups can be made on the fly on working DB
2019-07-19 11:17:12 +08:00
without freezing writers.
2017-01-18 21:40:56 +08:00
2019-07-19 11:17:12 +08:00
9. No additional memory management, all done by basic OS services.
2017-07-04 17:07:05 +08:00
2018-07-30 07:55:13 +08:00
--------------------------------------------------------------------------------
2017-07-04 17:07:05 +08:00
2018-07-30 07:55:13 +08:00
Improvements over LMDB
======================
2017-11-15 23:47:39 +08:00
2019-03-06 05:40:53 +08:00
1. Automatic dynamic DB size management according to the parameters
specified by `mdbx_env_set_geometry()` function. Including
2018-07-30 07:55:13 +08:00
growth step and truncation threshold, as well as the choice of page
size.
2017-07-04 17:07:05 +08:00
2019-03-06 05:40:53 +08:00
2. Automatic returning of freed pages into unallocated space at the end
of database file, with optionally automatic shrinking it. This reduces
2018-07-30 07:55:13 +08:00
amount of pages resides in RAM and circulated in disk I/O. In fact
_libmdbx_ constantly performs DB compactification, without spending
additional resources for that.
2017-07-04 17:07:05 +08:00
2019-03-06 05:40:53 +08:00
3. `LIFO RECLAIM` mode:
2017-07-04 17:07:05 +08:00
2018-07-30 07:55:13 +08:00
The newest pages are picked for reuse instead of the oldest. This allows
to minimize reclaim loop and make it execution time independent of total
page count.
2017-07-04 17:07:05 +08:00
2018-07-30 07:55:13 +08:00
This results in OS kernel cache mechanisms working with maximum
efficiency. In case of using disk controllers or storages with
[BBWC ](https://en.wikipedia.org/wiki/Disk_buffer#Write_acceleration )
this may greatly improve write performance.
2017-07-04 17:07:05 +08:00
2019-03-06 05:40:53 +08:00
4. Fast estimation of range query result size via functions `mdbx_estimate_range()` ,
`mdbx_estimate_move()` and `mdbx_estimate_distance()` . E.g. for selection the
optimal query execution plan.
5. `mdbx_chk` tool for DB integrity check.
2019-07-19 01:19:44 +08:00
6. Support for keys and values of zero length, including multi-values (aka sorted duplicates).
2019-03-06 05:40:53 +08:00
2019-07-16 08:16:25 +08:00
7. Ability to assign up to 3 persistent 64-bit markers to commiting transaction with
2019-03-06 05:40:53 +08:00
`mdbx_canary_put()` and then get them in read transaction by
`mdbx_canary_get()` .
2019-07-19 01:19:44 +08:00
8. Ability to update or delete record and get previous value via `mdbx_replace()` .
Also allows update the specific item from multi-value with the same key.
2019-03-06 05:40:53 +08:00
9. Sequence generation via `mdbx_dbi_sequence()` .
2017-07-04 17:07:05 +08:00
2019-03-06 05:40:53 +08:00
10. `OOM-KICK` callback.
2017-11-15 23:47:39 +08:00
2018-07-30 07:55:13 +08:00
`mdbx_env_set_oomfunc()` allows to set a callback, which will be called
in the event of DB space exhausting during long-time read transaction in
parallel with extensive updating. Callback will be invoked with PID and
pthread_id of offending thread as parameters. Callback can do any of
these things to remedy the problem:
2017-07-04 17:07:05 +08:00
2018-07-30 07:55:13 +08:00
* wait for read transaction to finish normally;
2017-07-04 17:07:05 +08:00
2018-07-30 07:55:13 +08:00
* kill the offending process (signal 9), if separate process is doing
long-time read;
2017-07-04 17:07:05 +08:00
2018-07-30 07:55:13 +08:00
* abort or restart offending read transaction if it's running in sibling
thread;
2017-11-15 23:47:39 +08:00
2018-07-30 07:55:13 +08:00
* abort current write transaction with returning error code.
2017-07-04 17:07:05 +08:00
2019-07-19 01:19:44 +08:00
11. Ability to open DB in exclusive mode by `MDBX_EXCLUSIVE` flag.
2017-07-04 17:07:05 +08:00
2019-07-19 01:19:44 +08:00
12. Ability to get how far current read-transaction snapshot lags
from the latest version of the DB by `mdbx_txn_straggler()` .
2017-07-04 17:07:05 +08:00
2019-07-19 01:19:44 +08:00
13. Ability to explicitly update the existing record, not insertion
a new one. Implemented as `MDBX_CURRENT` flag for `mdbx_put()` .
2017-07-04 17:07:05 +08:00
2019-03-06 05:40:53 +08:00
14. Fixed `mdbx_cursor_count()` , which returns correct count of
2019-07-19 01:19:44 +08:00
duplicated (aka multi-value) for all cases and any cursor position.
2017-01-09 06:19:24 +08:00
2019-03-06 05:40:53 +08:00
15. `mdbx_env_info()` to getting additional info, including number of
2019-07-19 01:19:44 +08:00
the oldest snapshot of DB, which is used by someone of the readers.
2017-01-09 06:19:24 +08:00
2019-03-06 05:40:53 +08:00
16. `mdbx_del()` doesn't ignore additional argument (specifier) `data`
2018-07-30 07:55:13 +08:00
for tables without duplicates (without flag `MDBX_DUPSORT` ), if `data`
is not null then always uses it to verify record, which is being
deleted.
2017-11-15 23:47:39 +08:00
2019-07-19 01:19:44 +08:00
17. Ability to open dbi-table with simultaneous with race-free setup
of comparators for keys and values, via `mdbx_dbi_open_ex()` .
2017-01-09 06:19:24 +08:00
2019-07-19 01:19:44 +08:00
18. `mdbx_is_dirty()` to find out if given key or value is on dirty page, that
2018-07-30 07:55:13 +08:00
useful to avoid copy-out before updates.
2018-01-18 03:19:59 +08:00
2019-03-06 05:40:53 +08:00
19. Correct update of current record in `MDBX_CURRENT` mode of
2018-07-30 07:55:13 +08:00
`mdbx_cursor_put()` , including sorted duplicated.
2018-01-18 03:19:59 +08:00
2019-03-06 05:40:53 +08:00
20. Check if there is a row with data after current cursor position via
2018-07-30 07:55:13 +08:00
`mdbx_cursor_eof()` .
2018-01-18 03:19:59 +08:00
2019-03-06 05:40:53 +08:00
21. Additional error code `MDBX_EMULTIVAL` , which is returned by
2018-07-30 07:55:13 +08:00
`mdbx_put()` and `mdbx_replace()` in case is ambiguous update or delete.
2018-01-18 03:19:59 +08:00
2019-03-06 05:40:53 +08:00
22. Ability to get value by key and duplicates count by `mdbx_get_ex()` .
2018-01-18 03:19:59 +08:00
2019-03-06 05:40:53 +08:00
23. Functions `mdbx_cursor_on_first()` and `mdbx_cursor_on_last()` ,
2019-07-19 01:19:44 +08:00
which allows to check cursor is currently on first or last position
2018-07-30 07:55:13 +08:00
respectively.
2017-07-04 17:07:05 +08:00
2019-07-19 01:19:44 +08:00
24. Automatic creation of steady commit-points (flushing data to the
disk) when the volume of changes reaches a threshold, which can be
set by `mdbx_env_set_syncbytes()` .
2017-11-15 23:47:39 +08:00
2019-03-06 05:40:53 +08:00
25. Control over debugging and receiving of debugging messages via
2018-07-30 07:55:13 +08:00
`mdbx_setup_debug()` .
2017-07-04 17:07:05 +08:00
2019-07-19 01:19:44 +08:00
26. Function `mdbx_env_pgwalk()` for page-walking the DB.
2017-07-04 17:07:05 +08:00
2019-07-19 01:19:44 +08:00
27. Three meta-pages instead of two, that allows to guarantee
consistency of data when updating weak commit-points without the
risk of damaging the last steady commit-point.
2017-07-04 17:07:05 +08:00
2019-03-06 05:40:53 +08:00
28. Guarantee of DB integrity in `WRITEMAP+MAPSYNC` mode:
2018-07-30 07:55:13 +08:00
> Current _libmdbx_ gives a choice of safe async-write mode (default)
> and `UTTERLY_NOSYNC` mode which may result in full
> DB corruption during system crash as with LMDB. For details see
> [Data safety in async-write mode](#data-safety-in-async-write-mode).
2017-07-04 17:07:05 +08:00
2019-03-06 05:40:53 +08:00
29. Ability to close DB in "dirty" state (without data flush and
2018-07-30 07:55:13 +08:00
creation of steady synchronization point) via `mdbx_env_close_ex()` .
2017-07-21 06:59:05 +08:00
2019-03-06 05:40:53 +08:00
30. If read transaction is aborted via `mdbx_txn_abort()` or
2019-07-19 01:19:44 +08:00
`mdbx_txn_reset()` then DBI-handles, which were opened during it,
will not be closed or deleted. In several cases this allows
to avoid hard-to-debug errors.
2017-07-04 17:07:05 +08:00
2019-03-06 05:40:53 +08:00
31. All cursors in all read and write transactions can be reused by
2018-07-30 07:55:13 +08:00
`mdbx_cursor_renew()` and MUST be freed explicitly.
> ## Caution, please pay attention!
>
> This is the only change of API, which changes semantics of cursor management
> and can lead to memory leaks on misuse. This is a needed change as it eliminates ambiguity
> which helps to avoid such errors as:
> - use-after-free;
> - double-free;
> - memory corruption and segfaults.
2017-11-15 23:47:39 +08:00
2017-07-04 17:07:05 +08:00
--------------------------------------------------------------------------------
2018-07-30 07:55:13 +08:00
## Gotchas
2017-07-04 17:07:05 +08:00
2019-07-19 01:19:44 +08:00
1. There cannot be more than one writer at a time. This allows serialize an
updates and eliminate any possibility of conflicts, deadlocks or logical errors.
2018-07-30 07:55:13 +08:00
2. No [WAL ](https://en.wikipedia.org/wiki/Write-ahead_logging ) means
relatively big [WAF ](https://en.wikipedia.org/wiki/Write_amplification )
(Write Amplification Factor). Because of this syncing data to disk might
be quite resource intensive and be main performance bottleneck during
intensive write workload.
> As compromise _libmdbx_ allows several modes of lazy and/or periodic
> syncing, including `MAPASYNC` mode, which modificate data in memory and
> asynchronously syncs data to disk, moment to sync is picked by OS.
>
> Although this should be used with care, synchronous transactions in a DB
> with transaction journal will require 2 IOPS minimum (probably 3-4 in
> practice) because of filesystem overhead, overhead depends on
> filesystem, not on record count or record size. In _libmdbx_ IOPS count
> will grow logarithmically depending on record count in DB (height of B+
> tree) and will require at least 2 IOPS per transaction too.
3. [CoW ](https://en.wikipedia.org/wiki/Copy-on-write ) for
[MVCC ](https://en.wikipedia.org/wiki/Multiversion_concurrency_control )
is done on memory page level with
[B+trees ](https://ru.wikipedia.org/wiki/B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE ).
Therefore altering data requires to copy about Olog(N) memory pages,
which uses [memory bandwidth ](https://en.wikipedia.org/wiki/Memory_bandwidth ) and is main
performance bottleneck in `MAPASYNC` mode.
> This is unavoidable, but isn't that bad. Syncing data to disk requires
> much more similar operations which will be done by OS, therefore this is
> noticeable only if data sync to persistent storage is fully disabled.
> _libmdbx_ allows to safely save data to persistent storage with minimal
> performance overhead. If there is no need to save data to persistent
> storage then it's much more preferable to use `std::map`.
2019-07-16 08:16:25 +08:00
4. _LMDB_ has a problem of long-time readers which degrades performance
2018-07-30 07:55:13 +08:00
and bloats DB.
> _libmdbx_ addresses that, details below.
2017-07-04 17:07:05 +08:00
2018-07-30 07:55:13 +08:00
5. _LMDB_ is susceptible to DB corruption in `WRITEMAP+MAPASYNC` mode.
_libmdbx_ in `WRITEMAP+MAPASYNC` guarantees DB integrity and consistency
of data.
> Additionally there is an alternative: `UTTERLY_NOSYNC` mode.
> Details below.
2017-07-04 17:07:05 +08:00
2019-07-16 08:16:25 +08:00
#### Problem of long-time reading
2018-07-30 07:55:13 +08:00
Garbage collection problem exists in all databases one way or another
(e.g. VACUUM in PostgreSQL). But in _libmdbx_ and LMDB it's even more
2019-07-16 08:16:25 +08:00
discernible because of high transaction rate and intentional internals
simplification in favor of performance.
2017-07-04 17:07:05 +08:00
2019-07-16 08:16:25 +08:00
Understanding the problem requires some explanation, but can be
difficult for quick perception. So is is reasonable
to simplify this as follows:
2017-07-04 17:07:05 +08:00
2019-07-16 08:16:25 +08:00
* Massive altering of data during a parallel long read operation may
exhaust the free DB space.
2017-11-15 23:47:39 +08:00
2019-07-16 08:16:25 +08:00
* If the available space is exhausted, any attempt to update the data
2019-07-19 11:17:12 +08:00
will cause a "MAP_FULL" error until a long read transaction is completed.
2019-07-16 08:16:25 +08:00
* A good example of long readers is a hot backup or debugging of
a client application while retaining an active read transaction.
2017-07-04 17:07:05 +08:00
2018-07-30 07:55:13 +08:00
* In _LMDB_ this results in degraded performance of all operations of
2019-07-16 08:16:25 +08:00
writing data to persistent storage.
* _libmdbx_ has the `OOM-KICK` mechanism which allow to abort such
operations and the `LIFO RECLAIM` mode which addresses performance
degradation.
#### Durability in asynchronous writing mode
In `WRITEMAP+MAPSYNC` mode updated (aka dirty) pages are written
to persistent storage by the OS kernel. This means that if the
application fails, the OS kernel will finish writing all updated
data to disk and nothing will be lost.
However, in the case of hardware malfunction or OS kernel fatal error,
only some updated data can be written to disk and the database structure
is likely to be destroyed.
In such situation, DB is completely corrupted and can't be repaired.
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
_libmdbx_ addresses this by fully reimplementing write path of data:
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
* In `WRITEMAP+MAPSYNC` mode meta-data pages aren't updated in place,
instead their shadow copies are used and their updates are synced after
data is flushed to disk.
2018-01-18 03:19:59 +08:00
2019-07-16 08:16:25 +08:00
* During transaction commit _libmdbx_ marks it as a steady or weak
depending on synchronization status between RAM and persistent storage.
For instance, in the `WRITEMAP+MAPSYNC` mode committed transactions
are marked as weak by default, but as steady after explicit data flushes.
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
* _libmdbx_ maintains three separate meta-pages instead of two. This
2019-07-16 08:16:25 +08:00
allows to commit transaction as steady or weak without losing two
previous commit points (one of them can be steady, and another
weak). Thus, after a fatal system failure, it will be possible to
rollback to the last steady commit point.
* During DB open _libmdbx_ rollbacks to the last steady commit point,
this guarantees database integrity after a crash. However, if the
database opening in read-only mode, such rollback cannot be performed
which will cause returning the MDBX_WANNA_RECOVERY error.
For data integrity a pages which form database snapshot with steady
commit point, must not be updated until next steady commit point.
Therefore the last steady commit point creates an effect analogues to "long-time read".
The only difference that now in case of space exhaustion the problem
will be immediately addressed by writing changes to disk and forming
the new steady commit point.
So in async-write mode _libmdbx_ will always use new pages until the
free DB space will be exhausted or `mdbx_env_sync()` will be invoked,
and the total write traffic to the disk will be the same as in sync-write mode.
Currently libmdbx gives a choice between a safe async-write mode (default) and
`UTTERLY_NOSYNC` mode which may lead to DB corruption after a system crash, i.e. like the LMDB.
Next version of _libmdbx_ will be automatically create steady commit
points in async-write mode upon completion transfer data to the disk.
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
--------------------------------------------------------------------------------
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
Performance comparison
======================
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
All benchmarks were done by [IOArena ](https://github.com/pmwkaa/ioarena )
and multiple [scripts ](https://github.com/pmwkaa/ioarena/tree/HL%2B%2B2015 )
runs on Lenovo Carbon-2 laptop, i7-4600U 2.1 GHz, 8 Gb RAM,
SSD SAMSUNG MZNTD512HAGL-000L1 (DXT23L0Q) 512 Gb.
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
--------------------------------------------------------------------------------
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
### Integral performance
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
Here showed sum of performance metrics in 3 benchmarks:
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
- Read/Search on 4 CPU cores machine;
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
- Transactions with [CRUD ](https://en.wikipedia.org/wiki/CRUD )
operations in sync-write mode (fdatasync is called after each
transaction);
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
- Transactions with [CRUD ](https://en.wikipedia.org/wiki/CRUD )
operations in lazy-write mode (moment to sync data to persistent storage
is decided by OS).
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
*Reasons why asynchronous mode isn't benchmarked here:*
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
1. It doesn't make sense as it has to be done with DB engines, oriented
for keeping data in memory e.g. [Tarantool ](https://tarantool.io/ ),
[Redis ](https://redis.io/ )), etc.
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
2. Performance gap is too high to compare in any meaningful way.
2018-01-18 03:19:59 +08:00
2018-07-30 07:55:13 +08:00
![Comparison #1: Integral Performance ](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-1.png )
2017-01-18 21:40:56 +08:00
2017-07-04 17:07:05 +08:00
--------------------------------------------------------------------------------
2017-01-18 21:40:56 +08:00
2018-07-30 07:55:13 +08:00
### Read Scalability
2017-01-09 02:48:00 +08:00
2018-07-30 07:55:13 +08:00
Summary performance with concurrent read/search queries in 1-2-4-8
threads on 4 CPU cores machine.
2017-01-09 06:19:24 +08:00
2018-07-30 07:55:13 +08:00
![Comparison #2: Read Scalability ](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-2.png )
2017-01-09 06:19:24 +08:00
2018-07-30 07:55:13 +08:00
--------------------------------------------------------------------------------
2017-01-18 21:40:56 +08:00
2018-07-30 07:55:13 +08:00
### Sync-write mode
2017-01-18 21:40:56 +08:00
2018-07-30 07:55:13 +08:00
- Linear scale on left and dark rectangles mean arithmetic mean
transactions per second;
2017-01-18 21:40:56 +08:00
2018-07-30 07:55:13 +08:00
- Logarithmic scale on right is in seconds and yellow intervals mean
execution time of transactions. Each interval shows minimal and maximum
execution time, cross marks standard deviation.
2017-01-18 21:40:56 +08:00
2018-07-30 07:55:13 +08:00
**10,000 transactions in sync-write mode**. In case of a crash all data
is consistent and state is right after last successful transaction.
[fdatasync ](https://linux.die.net/man/2/fdatasync ) syscall is used after
each write transaction in this mode.
2017-01-18 21:40:56 +08:00
2018-07-30 07:55:13 +08:00
In the benchmark each transaction contains combined CRUD operations (2
inserts, 1 read, 1 update, 1 delete). Benchmark starts on empty database
and after full run the database contains 10,000 small key-value records.
2017-01-18 21:40:56 +08:00
2018-07-30 07:55:13 +08:00
![Comparison #3: Sync-write mode ](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-3.png )
2017-01-09 02:48:00 +08:00
2018-07-30 07:55:13 +08:00
--------------------------------------------------------------------------------
2017-01-09 02:48:00 +08:00
2018-07-30 07:55:13 +08:00
### Lazy-write mode
2017-01-09 02:48:00 +08:00
2018-07-30 07:55:13 +08:00
- Linear scale on left and dark rectangles mean arithmetic mean of
thousands transactions per second;
2017-01-09 02:48:00 +08:00
2018-07-30 07:55:13 +08:00
- Logarithmic scale on right in seconds and yellow intervals mean
execution time of transactions. Each interval shows minimal and maximum
execution time, cross marks standard deviation.
2017-01-09 02:48:00 +08:00
2018-07-30 07:55:13 +08:00
**100,000 transactions in lazy-write mode**. In case of a crash all data
is consistent and state is right after one of last transactions, but
transactions after it will be lost. Other DB engines use
[WAL ](https://en.wikipedia.org/wiki/Write-ahead_logging ) or transaction
journal for that, which in turn depends on order of operations in
journaled filesystem. _libmdbx_ doesn't use WAL and hands I/O operations
to filesystem and OS kernel (mmap).
2017-01-09 02:48:00 +08:00
2018-07-30 07:55:13 +08:00
In the benchmark each transaction contains combined CRUD operations (2
inserts, 1 read, 1 update, 1 delete). Benchmark starts on empty database
and after full run the database contains 100,000 small key-value
records.
2017-01-09 02:48:00 +08:00
2018-07-30 07:55:13 +08:00
![Comparison #4: Lazy-write mode ](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-4.png )
2017-01-09 02:48:00 +08:00
2018-07-30 07:55:13 +08:00
--------------------------------------------------------------------------------
2017-01-09 02:48:00 +08:00
2018-07-30 07:55:13 +08:00
### Async-write mode
2017-01-09 02:48:00 +08:00
2018-07-30 07:55:13 +08:00
- Linear scale on left and dark rectangles mean arithmetic mean of
thousands transactions per second;
2017-02-08 22:52:05 +08:00
2018-07-30 07:55:13 +08:00
- Logarithmic scale on right in seconds and yellow intervals mean
execution time of transactions. Each interval shows minimal and maximum
execution time, cross marks standard deviation.
2017-02-08 22:52:05 +08:00
2018-07-30 07:55:13 +08:00
**1,000,000 transactions in async-write mode**. In case of a crash all
data will be consistent and state will be right after one of last
transactions, but lost transaction count is much higher than in
lazy-write mode. All DB engines in this mode do as little writes as
possible on persistent storage. _libmdbx_ uses
[msync(MS_ASYNC) ](https://linux.die.net/man/2/msync ) in this mode.
2017-02-08 22:52:05 +08:00
2018-07-30 07:55:13 +08:00
In the benchmark each transaction contains combined CRUD operations (2
inserts, 1 read, 1 update, 1 delete). Benchmark starts on empty database
and after full run the database contains 10,000 small key-value records.
2017-02-08 22:52:05 +08:00
2018-07-30 07:55:13 +08:00
![Comparison #5: Async-write mode ](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-5.png )
2017-02-08 22:52:05 +08:00
2018-07-30 07:55:13 +08:00
--------------------------------------------------------------------------------
2017-02-08 22:52:05 +08:00
2018-07-30 07:55:13 +08:00
### Cost comparison
2017-02-08 22:52:05 +08:00
2018-07-30 07:55:13 +08:00
Summary of used resources during lazy-write mode benchmarks:
2017-02-18 03:02:07 +08:00
2018-07-30 07:55:13 +08:00
- Read and write IOPS;
2017-02-18 03:02:07 +08:00
2018-07-30 07:55:13 +08:00
- Sum of user CPU time and sys CPU time;
2017-07-04 17:07:05 +08:00
2018-07-30 07:55:13 +08:00
- Used space on persistent storage after the test and closed DB, but not
waiting for the end of all internal housekeeping operations (LSM
compactification, etc).
2017-07-04 17:07:05 +08:00
2018-07-30 07:55:13 +08:00
_ForestDB_ is excluded because benchmark showed it's resource
consumption for each resource (CPU, IOPS) much higher than other engines
which prevents to meaningfully compare it with them.
2017-07-04 17:07:05 +08:00
2018-07-30 07:55:13 +08:00
All benchmark data is gathered by
[getrusage() ](http://man7.org/linux/man-pages/man2/getrusage.2.html )
syscall and by scanning data directory.
2017-07-27 23:16:19 +08:00
2018-07-30 07:55:13 +08:00
![Comparison #6: Cost comparison ](https://raw.githubusercontent.com/wiki/leo-yuriev/libmdbx/img/perf-slide-6.png )
2017-07-27 23:38:08 +08:00
2017-07-27 23:16:19 +08:00
--------------------------------------------------------------------------------
```
$ objdump -f -h -j .text libmdbx.so
libmdbx.so: file format elf64-x86-64
architecture: i386:x86-64, flags 0x00000150:
HAS_SYMS, DYNAMIC, D_PAGED
2019-03-06 05:40:53 +08:00
start address 0x0000000000003870
2017-07-27 23:16:19 +08:00
Sections:
2018-01-08 23:49:21 +08:00
Idx Name Size VMA LMA File off Algn
2019-03-06 05:40:53 +08:00
11 .text 000173d4 0000000000003870 0000000000003870 00003870 2**4
2017-07-27 23:16:19 +08:00
CONTENTS, ALLOC, LOAD, READONLY, CODE
2018-01-08 23:49:21 +08:00
2017-07-27 23:16:19 +08:00
```