forked from lthn/blockchain
831 lines
62 KiB
Markdown
831 lines
62 KiB
Markdown
|
|
### The [repository now only mirrored on the 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 -->
|
|||
|
|
-----
|
|||
|
|
|
|||
|
|
libmdbx
|
|||
|
|
======================================
|
|||
|
|
Доработанный и расширенный потомок [Lightning Memory-Mapped Database](https://ru.bmstu.wiki/LMDB_(Lightning_Memory-Mapped_Database)) (aka _LMDB_).
|
|||
|
|
English version is [here](README.md).
|
|||
|
|
|
|||
|
|
_libmdbx_ превосходит LMDB по возможностям и надежности, не уступая в
|
|||
|
|
производительности. _libmdbx_ работает на Linux, FreeBSD, MacOS X и
|
|||
|
|
других ОС соответствующих POSIX.1-2008, а также поддерживает Windows в
|
|||
|
|
качестве дополнительной платформы.
|
|||
|
|
|
|||
|
|
Отдельно ведётся не-публичная разработка следующей версии, в которой
|
|||
|
|
будет кардинальное изменение как API, так и формата базы данных. Цель
|
|||
|
|
этой революции - обеспечение более четкого и надежного API, добавление
|
|||
|
|
новых функций, а также наделение базы данных новыми свойствами.
|
|||
|
|
|
|||
|
|
*Всё будет хорошо. The Future will (be) [Positive](https://www.ptsecurity.ru).*
|
|||
|
|
|
|||
|
|
[](https://travis-ci.org/leo-yuriev/libmdbx)
|
|||
|
|
[](https://ci.appveyor.com/project/leo-yuriev/libmdbx/branch/master)
|
|||
|
|
[](https://scan.coverity.com/projects/reopen-libmdbx)
|
|||
|
|
|
|||
|
|
## Содержание
|
|||
|
|
- [Обзор](#Обзор)
|
|||
|
|
- [Сравнение с другими базами данных](#Сравнение-с-другими-базами-данных)
|
|||
|
|
- [История & Выражение признательности](#История)
|
|||
|
|
- [Описание](#Описание)
|
|||
|
|
- [Ключевые свойства](#Ключевые-свойства)
|
|||
|
|
- [Доработки и усовершенствования относительно LMDB](#Доработки-и-усовершенствования-относительно-lmdb)
|
|||
|
|
- [Недостатки и Компромиссы](#Недостатки-и-Компромиссы)
|
|||
|
|
- [Проблема долгих чтений](#Проблема-долгих-чтений)
|
|||
|
|
- [Сохранность данных в режиме асинхронной фиксации](#Сохранность-данных-в-режиме-асинхронной-фиксации)
|
|||
|
|
- [Использование](#Использование)
|
|||
|
|
- [Сборка](#Сборка)
|
|||
|
|
- [Привязки к другим языкам](#Привязки-к-другим-языкам)
|
|||
|
|
- [Сравнение производительности](#Сравнение-производительности)
|
|||
|
|
- [Интегральная производительность](#Интегральная-производительность)
|
|||
|
|
- [Масштабируемость чтения](#Масштабируемость-чтения)
|
|||
|
|
- [Синхронная фиксация](#Синхронная-фиксация)
|
|||
|
|
- [Отложенная фиксация](#Отложенная-фиксация)
|
|||
|
|
- [Асинхронная фиксация](#Асинхронная-фиксация)
|
|||
|
|
- [Потребление ресурсов](#Потребление-ресурсов)
|
|||
|
|
|
|||
|
|
-----
|
|||
|
|
|
|||
|
|
## Обзор
|
|||
|
|
_libmdbx_ - это встраиваемый key-value движок хранения со специфическим
|
|||
|
|
набором свойств и возможностей, ориентированный на создание уникальных
|
|||
|
|
легковесных решений с предельной производительностью.
|
|||
|
|
|
|||
|
|
_libmdbx_ позволяет множеству процессов совместно читать и обновлять
|
|||
|
|
несколько key-value таблиц с соблюдением
|
|||
|
|
[ACID](https://ru.wikipedia.org/wiki/ACID), при минимальных накладных
|
|||
|
|
расходах и амортизационной стоимости любых операций Olog(N).
|
|||
|
|
|
|||
|
|
_libmdbx_ обеспечивает
|
|||
|
|
[serializability](https://en.wikipedia.org/wiki/Serializability)
|
|||
|
|
изменений и согласованность данных после аварий. При этом транзакции,
|
|||
|
|
изменяющие данные, никак не мешают операциям чтения и выполняются строго
|
|||
|
|
последовательно с использованием единственного
|
|||
|
|
[мьютекса](https://en.wikipedia.org/wiki/Mutual_exclusion).
|
|||
|
|
|
|||
|
|
_libmdbx_ позволяет выполнять операции чтения с гарантиями
|
|||
|
|
[wait-free](https://en.wikipedia.org/wiki/Non-blocking_algorithm#Wait-freedom),
|
|||
|
|
параллельно на каждом ядре CPU, без использования атомарных операций
|
|||
|
|
и/или примитивов синхронизации.
|
|||
|
|
|
|||
|
|
_libmdbx_ не использует
|
|||
|
|
[LSM](https://en.wikipedia.org/wiki/Log-structured_merge-tree), а
|
|||
|
|
основан на [B+Tree](https://en.wikipedia.org/wiki/B%2B_tree) с
|
|||
|
|
[отображением](https://en.wikipedia.org/wiki/Memory-mapped_file) всех
|
|||
|
|
данных в память, при этом текущая версия не использует
|
|||
|
|
[WAL](https://en.wikipedia.org/wiki/Write-ahead_logging). Это
|
|||
|
|
предопределяет многие свойства, в том числе удачные и противопоказанные
|
|||
|
|
сценарии использования.
|
|||
|
|
|
|||
|
|
|
|||
|
|
### Сравнение с другими базами данных
|
|||
|
|
|
|||
|
|
На данный момент, пожалуйста, обратитесь к [главе "сравнение BoltDB с
|
|||
|
|
другими базами
|
|||
|
|
данных"](https://github.com/coreos/bbolt#comparison-with-other-databases),
|
|||
|
|
которая также (в основном) применима к MDBX.
|
|||
|
|
|
|||
|
|
|
|||
|
|
### История
|
|||
|
|
_libmdbx_ является результатом переработки и развития "Lightning
|
|||
|
|
Memory-Mapped Database", известной под аббревиатурой
|
|||
|
|
[LMDB](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database).
|
|||
|
|
Изначально доработка производилась в составе проекта
|
|||
|
|
[ReOpenLDAP](https://github.com/leo-yuriev/ReOpenLDAP). Примерно за год
|
|||
|
|
работы внесенные изменения приобрели самостоятельную ценность. Осенью
|
|||
|
|
2015 доработанный движок был выделен в отдельный проект, который был
|
|||
|
|
[представлен на конференции Highload++
|
|||
|
|
2015](http://www.highload.ru/2015/abstracts/1831.html).
|
|||
|
|
|
|||
|
|
В начале 2017 года движок _libmdbx_ получил новый импульс развития,
|
|||
|
|
благодаря использованию в [Fast Positive
|
|||
|
|
Tables](https://github.com/leo-yuriev/libfpta), aka ["Позитивные
|
|||
|
|
Таблицы"](https://github.com/leo-yuriev/libfpta) by [Positive
|
|||
|
|
Technologies](https://www.ptsecurity.ru).
|
|||
|
|
|
|||
|
|
|
|||
|
|
### Выражение признательности
|
|||
|
|
|
|||
|
|
Говард Чу (Howard Chu) <hyc@openldap.org> является автором движка LMDB, от
|
|||
|
|
которого в 2015 году произошел MDBX.
|
|||
|
|
|
|||
|
|
Мартин Хеденфальк (Martin Hedenfalk) <martin@bzero.se> является автором кода
|
|||
|
|
`btree.c`, который использовался для начала разработки LMDB.
|
|||
|
|
|
|||
|
|
-----
|
|||
|
|
|
|||
|
|
Описание
|
|||
|
|
========
|
|||
|
|
|
|||
|
|
## Ключевые свойства
|
|||
|
|
|
|||
|
|
_libmdbx_ наследует все ключевые возможности и особенности своего
|
|||
|
|
прародителя
|
|||
|
|
[LMDB](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database),
|
|||
|
|
но с устранением ряда описываемых далее проблем и архитектурных
|
|||
|
|
недочетов.
|
|||
|
|
|
|||
|
|
1. Данные хранятся в упорядоченном отображении (ordered map), ключи
|
|||
|
|
всегда отсортированы, поддерживается выборка диапазонов (range lookups).
|
|||
|
|
|
|||
|
|
2. Данные отображается в память каждого работающего с БД процесса. К
|
|||
|
|
данным и ключам обеспечивается прямой доступ в памяти без необходимости
|
|||
|
|
их копирования.
|
|||
|
|
|
|||
|
|
3. Транзакции согласно [ACID](https://ru.wikipedia.org/wiki/ACID),
|
|||
|
|
посредством [MVCC](https://ru.wikipedia.org/wiki/MVCC) и
|
|||
|
|
[COW](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BF%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B8).
|
|||
|
|
Изменения строго последовательны и не блокируются чтением, конфликты
|
|||
|
|
между транзакциями невозможны. При этом гарантируется чтение только
|
|||
|
|
зафиксированных данных, см [relaxing
|
|||
|
|
serializability](https://en.wikipedia.org/wiki/Serializability).
|
|||
|
|
|
|||
|
|
4. Чтение и поиск [без
|
|||
|
|
блокировок](https://ru.wikipedia.org/wiki/%D0%9D%D0%B5%D0%B1%D0%BB%D0%BE%D0%BA%D0%B8%D1%80%D1%83%D1%8E%D1%89%D0%B0%D1%8F_%D1%81%D0%B8%D0%BD%D1%85%D1%80%D0%BE%D0%BD%D0%B8%D0%B7%D0%B0%D1%86%D0%B8%D1%8F),
|
|||
|
|
без [атомарных
|
|||
|
|
операций](https://ru.wikipedia.org/wiki/%D0%90%D1%82%D0%BE%D0%BC%D0%B0%D1%80%D0%BD%D0%B0%D1%8F_%D0%BE%D0%BF%D0%B5%D1%80%D0%B0%D1%86%D0%B8%D1%8F).
|
|||
|
|
Читатели не блокируются операциями записи и не конкурируют между собой,
|
|||
|
|
чтение масштабируется линейно по ядрам CPU.
|
|||
|
|
> Для точности следует отметить, что "подключение к БД" (старт первой
|
|||
|
|
> читающей транзакции в потоке) и "отключение от БД" (закрытие БД или
|
|||
|
|
> завершение потока) требуют краткосрочного захвата блокировки для
|
|||
|
|
> регистрации/дерегистрации текущего потока в "таблице читателей".
|
|||
|
|
|
|||
|
|
5. Эффективное хранение дубликатов (ключей с несколькими значениями),
|
|||
|
|
без дублирования ключей, с сортировкой значений, в том числе
|
|||
|
|
целочисленных (для вторичных индексов).
|
|||
|
|
|
|||
|
|
6. Эффективная поддержка коротких ключей фиксированной длины, в том
|
|||
|
|
числе целочисленных.
|
|||
|
|
|
|||
|
|
7. Амортизационная стоимость любой операции Olog(N),
|
|||
|
|
[WAF](https://en.wikipedia.org/wiki/Write_amplification) (Write
|
|||
|
|
Amplification Factor) и RAF (Read Amplification Factor) также Olog(N).
|
|||
|
|
|
|||
|
|
8. Нет [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) и
|
|||
|
|
журнала транзакций, после сбоев не требуется восстановление. Не
|
|||
|
|
требуется компактификация или какое-либо периодическое обслуживание.
|
|||
|
|
Поддерживается резервное копирование "по горячему", на работающей БД без
|
|||
|
|
приостановки изменения данных.
|
|||
|
|
|
|||
|
|
9. Отсутствует какое-либо внутреннее управление памятью или
|
|||
|
|
кэшированием. Всё необходимое штатно выполняет ядро ОС.
|
|||
|
|
|
|||
|
|
|
|||
|
|
## Доработки и усовершенствования относительно LMDB
|
|||
|
|
|
|||
|
|
1. Автоматическое динамическое управление размером БД согласно
|
|||
|
|
параметрам задаваемым функцией `mdbx_env_set_geometry()`, включая шаг
|
|||
|
|
приращения и порог уменьшения размера БД, а также выбор размера
|
|||
|
|
страницы. Соответственно, это позволяет снизить фрагментированность
|
|||
|
|
файла БД на диске и освободить место, в том числе в **Windows**.
|
|||
|
|
|
|||
|
|
2. Автоматическая без-затратная компактификация БД путем возврата
|
|||
|
|
освобождающихся страниц в область нераспределенного резерва в конце
|
|||
|
|
файла данных. При этом уменьшается количество страниц находящихся в
|
|||
|
|
памяти и участвующих в в обмене с диском.
|
|||
|
|
|
|||
|
|
3. Режим `LIFO RECLAIM`.
|
|||
|
|
|
|||
|
|
Для повторного использования выбираются не самые старые, а
|
|||
|
|
самые новые страницы из доступных. За счет этого цикл
|
|||
|
|
использования страниц всегда имеет минимальную длину и не
|
|||
|
|
зависит от общего числа выделенных страниц.
|
|||
|
|
|
|||
|
|
В результате механизмы кэширования и обратной записи работают с
|
|||
|
|
максимально возможной эффективностью. В случае использования
|
|||
|
|
контроллера дисков или системы хранения с
|
|||
|
|
[BBWC](https://en.wikipedia.org/wiki/BBWC) возможно
|
|||
|
|
многократное увеличение производительности по записи
|
|||
|
|
(обновлению данных).
|
|||
|
|
|
|||
|
|
4. Быстрая оценка количества элементов попадающих в запрашиваемый
|
|||
|
|
диапазон значений ключа посредством функций `mdbx_estimate_range()`,
|
|||
|
|
`mdbx_estimate_move()` и `mdbx_estimate_distance()` для выбора
|
|||
|
|
оптимального плана выполнения запроса.
|
|||
|
|
|
|||
|
|
5. Утилита `mdbx_chk` для проверки целостности структуры БД.
|
|||
|
|
|
|||
|
|
6. Поддержка ключей и значений нулевой длины, включая сортированные
|
|||
|
|
дубликаты.
|
|||
|
|
|
|||
|
|
7. Возможность связать с каждой завершаемой транзакцией до 3
|
|||
|
|
дополнительных маркеров посредством `mdbx_canary_put()`, и прочитать их
|
|||
|
|
в транзакции чтения посредством `mdbx_canary_get()`.
|
|||
|
|
|
|||
|
|
8. Возможность посредством `mdbx_replace()` обновить или удалить запись
|
|||
|
|
с получением предыдущего значения данных, а также адресно изменить
|
|||
|
|
конкретное multi-значение.
|
|||
|
|
|
|||
|
|
9. Генерация последовательностей посредством `mdbx_dbi_sequence()`.
|
|||
|
|
|
|||
|
|
10. Обработчик `OOM-KICK`.
|
|||
|
|
|
|||
|
|
Посредством `mdbx_env_set_oomfunc()` может быть установлен
|
|||
|
|
внешний обработчик (callback), который будет вызван при
|
|||
|
|
исчерпании свободных страниц по причине долгой операцией чтения
|
|||
|
|
на фоне интенсивного изменения данных.
|
|||
|
|
Обработчику будет передан PID и pthread_id виновника.
|
|||
|
|
В свою очередь обработчик может предпринять одно из действий:
|
|||
|
|
|
|||
|
|
* нейтрализовать виновника (отправить сигнал kill #9), если
|
|||
|
|
долгое чтение выполняется сторонним процессом;
|
|||
|
|
|
|||
|
|
* отменить или перезапустить проблемную операцию чтения, если
|
|||
|
|
операция выполняется одним из потоков текущего процесса;
|
|||
|
|
|
|||
|
|
* подождать некоторое время, в расчете на то, что проблемная операция
|
|||
|
|
чтения будет штатно завершена;
|
|||
|
|
|
|||
|
|
* прервать текущую операцию изменения данных с возвратом кода
|
|||
|
|
ошибки.
|
|||
|
|
|
|||
|
|
11. Возможность открыть БД в эксклюзивном режиме посредством флага
|
|||
|
|
`MDBX_EXCLUSIVE`, в том числе на сетевом носителе.
|
|||
|
|
|
|||
|
|
12. Возможность получить отставание текущей транзакции чтения от
|
|||
|
|
последней версии данных в БД посредством `mdbx_txn_straggler()`.
|
|||
|
|
|
|||
|
|
13. Возможность явно запросить обновление существующей записи, без
|
|||
|
|
создания новой посредством флажка `MDBX_CURRENT` для `mdbx_put()`.
|
|||
|
|
|
|||
|
|
14. Исправленный вариант `mdbx_cursor_count()`, возвращающий корректное
|
|||
|
|
количество дубликатов для всех типов таблиц и любого положения курсора.
|
|||
|
|
|
|||
|
|
15. Возможность получить посредством `mdbx_env_info()` дополнительную
|
|||
|
|
информацию, включая номер самой старой версии БД (снимка данных),
|
|||
|
|
который используется одним из читателей.
|
|||
|
|
|
|||
|
|
16. Функция `mdbx_del()` не игнорирует дополнительный (уточняющий)
|
|||
|
|
аргумент `data` для таблиц без дубликатов (без флажка `MDBX_DUPSORT`), а
|
|||
|
|
при его ненулевом значении всегда использует его для сверки с удаляемой
|
|||
|
|
записью.
|
|||
|
|
|
|||
|
|
17. Возможность открыть dbi-таблицу, одновременно с установкой
|
|||
|
|
компараторов для ключей и данных, посредством `mdbx_dbi_open_ex()`.
|
|||
|
|
|
|||
|
|
18. Возможность посредством `mdbx_is_dirty()` определить находятся ли
|
|||
|
|
некоторый ключ или данные в "грязной" странице БД. Таким образом,
|
|||
|
|
избегая лишнего копирования данных перед выполнением модифицирующих
|
|||
|
|
операций (значения, размещенные в "грязных" страницах, могут быть
|
|||
|
|
перезаписаны при изменениях, иначе они будут неизменны).
|
|||
|
|
|
|||
|
|
19. Корректное обновление текущей записи, в том числе сортированного
|
|||
|
|
дубликата, при использовании режима `MDBX_CURRENT` в
|
|||
|
|
`mdbx_cursor_put()`.
|
|||
|
|
|
|||
|
|
20. Возможность узнать есть ли за текущей позицией курсора строка данных
|
|||
|
|
посредством `mdbx_cursor_eof()`.
|
|||
|
|
|
|||
|
|
21. Дополнительный код ошибки `MDBX_EMULTIVAL`, который возвращается из
|
|||
|
|
`mdbx_put()` и `mdbx_replace()` при попытке выполнить неоднозначное
|
|||
|
|
обновление или удаления одного из нескольких значений с одним ключом.
|
|||
|
|
|
|||
|
|
22. Возможность посредством `mdbx_get_ex()` получить значение по
|
|||
|
|
заданному ключу, одновременно с количеством дубликатов.
|
|||
|
|
|
|||
|
|
23. Наличие функций `mdbx_cursor_on_first()` и `mdbx_cursor_on_last()`,
|
|||
|
|
которые позволяют быстро выяснить стоит ли курсор на первой/последней
|
|||
|
|
позиции.
|
|||
|
|
|
|||
|
|
24. Возможность автоматического формирования контрольных точек (сброса
|
|||
|
|
данных на диск) при накоплении заданного объёма изменений,
|
|||
|
|
устанавливаемого функцией `mdbx_env_set_syncbytes()`.
|
|||
|
|
|
|||
|
|
25. Управление отладкой и получение отладочных сообщений посредством
|
|||
|
|
`mdbx_setup_debug()`.
|
|||
|
|
|
|||
|
|
26. Функция `mdbx_env_pgwalk()` для обхода всех страниц БД.
|
|||
|
|
|
|||
|
|
27. Три мета-страницы вместо двух, что позволяет гарантированно
|
|||
|
|
консистентно обновлять слабые контрольные точки фиксации без риска
|
|||
|
|
повредить крайнюю сильную точку фиксации.
|
|||
|
|
|
|||
|
|
28. Гарантия сохранности БД в режиме `WRITEMAP+MAPSYNC`.
|
|||
|
|
> В текущей версии _libmdbx_ вам предоставляется выбор между безопасным
|
|||
|
|
> режимом (по умолчанию) асинхронной фиксации, и режимом `UTTERLY_NOSYNC`
|
|||
|
|
> когда при системной аварии есть шанс полного разрушения БД как в LMDB.
|
|||
|
|
> Для подробностей смотрите раздел
|
|||
|
|
> [Сохранность данных в режиме асинхронной фиксации](#Сохранность-данных-в-режиме-асинхронной-фиксации).
|
|||
|
|
|
|||
|
|
29. Возможность закрыть БД в "грязном" состоянии (без сброса данных и
|
|||
|
|
формирования сильной точки фиксации) посредством `mdbx_env_close_ex()`.
|
|||
|
|
|
|||
|
|
30. При завершении читающих транзакций, открытые в них DBI-хендлы не
|
|||
|
|
закрываются и не теряются при завершении таких транзакций посредством
|
|||
|
|
`mdbx_txn_abort()` или `mdbx_txn_reset()`. Что позволяет избавится от ряда
|
|||
|
|
сложно обнаруживаемых ошибок.
|
|||
|
|
|
|||
|
|
31. Все курсоры, как в транзакциях только для чтения, так и в пишущих,
|
|||
|
|
могут быть переиспользованы посредством `mdbx_cursor_renew()` и ДОЛЖНЫ
|
|||
|
|
ОСВОБОЖДАТЬСЯ ЯВНО.
|
|||
|
|
>
|
|||
|
|
> ## _ВАЖНО_, Обратите внимание!
|
|||
|
|
>
|
|||
|
|
> Это единственное изменение в API, которое значимо меняет
|
|||
|
|
> семантику управления курсорами и может приводить к утечкам
|
|||
|
|
> памяти. Следует отметить, что это изменение вынужденно.
|
|||
|
|
> Так устраняется неоднозначность с массой тяжких последствий:
|
|||
|
|
>
|
|||
|
|
> - обращение к уже освобожденной памяти;
|
|||
|
|
> - попытки повторного освобождения памяти;
|
|||
|
|
> - повреждение памяти и ошибки сегментации.
|
|||
|
|
|
|||
|
|
32. На **MacOS X** для синхронизации данных с диском _по-умолчанию_
|
|||
|
|
используется системная функция `fcntl(F_FULLFSYNC)`, так как [только
|
|||
|
|
этим гарантируется сохранность
|
|||
|
|
данных](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fsync.2.html)
|
|||
|
|
при сбое электропитания. К сожалению, в сценариях с высокой
|
|||
|
|
интенсивностью пишущих транзакций, использование `F_FULLFSYNC` приводит
|
|||
|
|
к существенной деградации производительности в сравнении с LMDB, где
|
|||
|
|
используется системная функция `fsync()`. Поэтому _libmdbx_ позволяет
|
|||
|
|
переопределить это поведение определением опции
|
|||
|
|
`MDBX_OSX_SPEED_INSTEADOF_DURABILITY=1` при сборке библиотеки.
|
|||
|
|
|
|||
|
|
33. На **Windows** _libmdbx_ использует файловые блокировки
|
|||
|
|
`LockFileEx()`, так как это позволяет размещать БД на сетевых дисках, а
|
|||
|
|
также обеспечивает защиту от некомпетентных действий пользователя
|
|||
|
|
([защиту от
|
|||
|
|
дурака](https://ru.wikipedia.org/wiki/%D0%97%D0%B0%D1%89%D0%B8%D1%82%D0%B0_%D0%BE%D1%82_%D0%B4%D1%83%D1%80%D0%B0%D0%BA%D0%B0)).
|
|||
|
|
Поэтому _libmdbx_ может немного отставать в тестах производительность от
|
|||
|
|
LMDB, где используются именованные мьютексы.
|
|||
|
|
|
|||
|
|
|
|||
|
|
## Недостатки и Компромиссы
|
|||
|
|
|
|||
|
|
1. Единовременно может выполняться не более одной транзакция изменения данных
|
|||
|
|
(один писатель). Зато все изменения всегда последовательны, не может быть
|
|||
|
|
конфликтов или логических ошибок при откате транзакций.
|
|||
|
|
|
|||
|
|
2. Отсутствие [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging)
|
|||
|
|
обуславливает относительно большой
|
|||
|
|
[WAF](https://en.wikipedia.org/wiki/Write_amplification) (Write
|
|||
|
|
Amplification Factor). Поэтому фиксация изменений на диске может быть
|
|||
|
|
достаточно дорогой и являться главным ограничением производительности
|
|||
|
|
при интенсивном изменении данных.
|
|||
|
|
> В качестве компромисса _libmdbx_ предлагает несколько режимов ленивой
|
|||
|
|
> и/или периодической фиксации. В том числе режим `MAPASYNC`, при котором
|
|||
|
|
> изменения происходят только в памяти и асинхронно фиксируются на диске
|
|||
|
|
> ядром ОС.
|
|||
|
|
>
|
|||
|
|
> Однако, следует воспринимать это свойство аккуратно и взвешенно.
|
|||
|
|
> Например, полная фиксация транзакции в БД с журналом потребует минимум 2
|
|||
|
|
> IOPS (скорее всего 3-4) из-за накладных расходов в файловой системе. В
|
|||
|
|
> _libmdbx_ фиксация транзакции также требует от 2 IOPS. Однако, в БД с
|
|||
|
|
> журналом кол-во IOPS будет меняться в зависимости от файловой системы,
|
|||
|
|
> но не от кол-ва записей или их объема. Тогда как в _libmdbx_ кол-во
|
|||
|
|
> будет расти логарифмически от кол-ва записей/строк в БД (по высоте
|
|||
|
|
> b+tree).
|
|||
|
|
|
|||
|
|
3. [COW](https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D0%BF%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_%D0%BF%D1%80%D0%B8_%D0%B7%D0%B0%D0%BF%D0%B8%D1%81%D0%B8)
|
|||
|
|
для реализации [MVCC](https://ru.wikipedia.org/wiki/MVCC) выполняется на
|
|||
|
|
уровне страниц в [B+
|
|||
|
|
дереве](https://ru.wikipedia.org/wiki/B-%D0%B4%D0%B5%D1%80%D0%B5%D0%B2%D0%BE).
|
|||
|
|
Поэтому изменение данных амортизационно требует копирования Olog(N)
|
|||
|
|
страниц, что расходует [пропускную способность оперативной
|
|||
|
|
памяти](https://en.wikipedia.org/wiki/Memory_bandwidth) и является
|
|||
|
|
основным ограничителем производительности в режиме `MAPASYNC`.
|
|||
|
|
> Этот недостаток неустраним, тем не менее следует дать некоторые пояснения.
|
|||
|
|
> Дело в том, что фиксация изменений на диске потребует гораздо более
|
|||
|
|
> значительного копирования данных в памяти и массы других затратных операций.
|
|||
|
|
> Поэтому обусловленное этим недостатком падение производительности становится
|
|||
|
|
> заметным только при отказе от фиксации изменений на диске.
|
|||
|
|
> Соответственно, корректнее сказать, что _libmdbx_ позволяет
|
|||
|
|
> получить персистентность ценой минимального падения производительности.
|
|||
|
|
> Если же нет необходимости оперативно сохранять данные, то логичнее
|
|||
|
|
> использовать `std::map`.
|
|||
|
|
|
|||
|
|
4. В _LMDB_ существует проблема долгих чтений (приостановленных читателей),
|
|||
|
|
которая приводит к деградации производительности и переполнению БД.
|
|||
|
|
> В _libmdbx_ предложены средства для предотвращения, быстрого выхода из
|
|||
|
|
> некомфортной ситуации и устранения её последствий. Подробности ниже.
|
|||
|
|
|
|||
|
|
5. В _LMDB_ есть вероятность разрушения БД в режиме `WRITEMAP+MAPASYNC`.
|
|||
|
|
В _libmdbx_ для `WRITEMAP+MAPASYNC` гарантируется как сохранность базы,
|
|||
|
|
так и согласованность данных.
|
|||
|
|
> Дополнительно, в качестве альтернативы, предложен режим `UTTERLY_NOSYNC`.
|
|||
|
|
> Подробности ниже.
|
|||
|
|
|
|||
|
|
|
|||
|
|
### Проблема долгих чтений
|
|||
|
|
*Следует отметить*, что проблема "сборки мусора" так или иначе
|
|||
|
|
существует во всех СУБД (Vacuum в PostgreSQL). Однако в случае _libmdbx_
|
|||
|
|
и LMDB она проявляется более остро, прежде всего из-за высокой
|
|||
|
|
производительности, а также из-за намеренного упрощения внутренних
|
|||
|
|
механизмов ради производительности.
|
|||
|
|
|
|||
|
|
Понимание проблемы требует некоторых пояснений, которые
|
|||
|
|
изложены ниже, но могут быть сложны для быстрого восприятия.
|
|||
|
|
Поэтому, тезисно:
|
|||
|
|
|
|||
|
|
* Изменение данных на фоне долгой операции чтения может
|
|||
|
|
приводить к исчерпанию места в БД.
|
|||
|
|
|
|||
|
|
* После чего любая попытка обновить данные будет приводить к
|
|||
|
|
ошибке `MAP_FULL` до завершения долгой операции чтения.
|
|||
|
|
|
|||
|
|
* Характерными примерами долгих чтений являются горячее
|
|||
|
|
резервное копирования и отладка клиентского приложения при
|
|||
|
|
активной транзакции чтения.
|
|||
|
|
|
|||
|
|
* В оригинальной _LMDB_ после этого будет наблюдаться
|
|||
|
|
устойчивая деградация производительности всех механизмов
|
|||
|
|
обратной записи на диск (в I/O контроллере, в гипервизоре,
|
|||
|
|
в ядре ОС).
|
|||
|
|
|
|||
|
|
* В _libmdbx_ предусмотрен механизм аварийного прерывания таких
|
|||
|
|
операций, а также режим `LIFO RECLAIM` устраняющий последующую
|
|||
|
|
деградацию производительности.
|
|||
|
|
|
|||
|
|
Операции чтения выполняются в контексте снимка данных (версии
|
|||
|
|
БД), который был актуальным на момент старта транзакции чтения. Такой
|
|||
|
|
читаемый снимок поддерживается неизменным до завершения операции. В свою
|
|||
|
|
очередь, это не позволяет повторно использовать страницы БД в
|
|||
|
|
последующих версиях (снимках БД).
|
|||
|
|
|
|||
|
|
Другими словами, если обновление данных выполняется на фоне долгой
|
|||
|
|
операции чтения, то вместо повторного использования "старых" ненужных
|
|||
|
|
страниц будут выделяться новые, так как "старые" страницы составляют
|
|||
|
|
снимок БД, который еще используется долгой операцией чтения.
|
|||
|
|
|
|||
|
|
В результате, при интенсивном изменении данных и достаточно длительной
|
|||
|
|
операции чтения, в БД могут быть исчерпаны свободные страницы, что не
|
|||
|
|
позволит создавать новые снимки/версии БД. Такая ситуация будет
|
|||
|
|
сохраняться до завершения операции чтения, которая использует старый
|
|||
|
|
снимок данных и препятствует повторному использованию страниц БД.
|
|||
|
|
|
|||
|
|
Однако, на этом проблемы не заканчиваются. После описанной ситуации, все
|
|||
|
|
дополнительные страницы, которые были выделены пока переработка старых
|
|||
|
|
была невозможна, будут участвовать в цикле выделения/освобождения до
|
|||
|
|
конца жизни экземпляра БД. В оригинальной _LMDB_ этот цикл использования
|
|||
|
|
страниц работает по принципу [FIFO](https://ru.wikipedia.org/wiki/FIFO).
|
|||
|
|
Поэтому увеличение количества циркулирующий страниц, с точки зрения
|
|||
|
|
механизмов кэширования и/или обратной записи, выглядит как увеличение
|
|||
|
|
рабочего набор данных. Проще говоря, однократное попадание в ситуацию
|
|||
|
|
"уснувшего читателя" приводит к устойчивому эффекту вымывания I/O кэша
|
|||
|
|
при всех последующих изменениях данных.
|
|||
|
|
|
|||
|
|
Для устранения описанных проблемы в _libmdbx_ сделаны существенные
|
|||
|
|
доработки, подробности ниже. Иллюстрации к проблеме "долгих чтений"
|
|||
|
|
можно найти в [слайдах презентации](http://www.slideshare.net/leoyuriev/lmdb).
|
|||
|
|
|
|||
|
|
Там же приведен пример количественной оценки прироста производительности
|
|||
|
|
за счет эффективной работы [BBWC](https://en.wikipedia.org/wiki/BBWC)
|
|||
|
|
при включении `LIFO RECLAIM` в _libmdbx_.
|
|||
|
|
|
|||
|
|
### Сохранность данных в режиме асинхронной фиксации
|
|||
|
|
При работе в режиме `WRITEMAP+MAPSYNC` запись измененных страниц
|
|||
|
|
выполняется ядром ОС, что имеет ряд преимуществ. Так например, при крахе
|
|||
|
|
приложения, ядро ОС сохранит все изменения.
|
|||
|
|
|
|||
|
|
Однако, при аварийном отключении питания или сбое в ядре ОС, на диске
|
|||
|
|
может быть сохранена только часть измененных страниц БД. При этом с
|
|||
|
|
большой вероятностью может оказаться, что будут сохранены мета-страницы
|
|||
|
|
со ссылками на страницы с новыми версиями данных, но не сами новые
|
|||
|
|
данные. В этом случае БД будет безвозвратна разрушена, даже если до
|
|||
|
|
аварии производилась полная синхронизация данных (посредством
|
|||
|
|
`mdbx_env_sync()`).
|
|||
|
|
|
|||
|
|
В _libmdbx_ эта проблема устранена путем полной переработки
|
|||
|
|
пути записи данных:
|
|||
|
|
|
|||
|
|
* В режиме `WRITEMAP+MAPSYNC` _libmdbx_ не обновляет
|
|||
|
|
мета-страницы непосредственно, а поддерживает их теневые копии
|
|||
|
|
с переносом изменений после фиксации данных.
|
|||
|
|
|
|||
|
|
* При завершении транзакций, в зависимости от состояния
|
|||
|
|
синхронности данных между диском и оперативной памятью,
|
|||
|
|
_libmdbx_ помечает точки фиксации либо как сильные (strong),
|
|||
|
|
либо как слабые (weak). Так например, в режиме
|
|||
|
|
`WRITEMAP+MAPSYNC` завершаемые транзакции помечаются как
|
|||
|
|
слабые, а при явной синхронизации данных - как сильные.
|
|||
|
|
|
|||
|
|
* В _libmdbx_ поддерживается не две, а три отдельные мета-страницы.
|
|||
|
|
Это позволяет выполнять фиксацию транзакций с формированием как
|
|||
|
|
сильной, так и слабой точки фиксации, без потери двух предыдущих
|
|||
|
|
точек фиксации (из которых одна может быть сильной, а вторая слабой).
|
|||
|
|
В результате, _libmdbx_ позволяет в произвольном порядке чередовать
|
|||
|
|
сильные и слабые точки фиксации без нарушения соответствующих
|
|||
|
|
гарантий в случае неожиданной системной аварии во время фиксации.
|
|||
|
|
|
|||
|
|
* При открытии БД выполняется автоматический откат к последней
|
|||
|
|
сильной фиксации. Этим обеспечивается гарантия сохранности БД.
|
|||
|
|
|
|||
|
|
Такая гарантия надежности не дается бесплатно. Для сохранности данных,
|
|||
|
|
страницы, формирующие крайний снимок с сильной фиксацией, не должны
|
|||
|
|
повторно использоваться (перезаписываться) до формирования следующей
|
|||
|
|
сильной точки фиксации. Таким образом, крайняя точка фиксации создает
|
|||
|
|
описанный выше эффект "долгого чтения". Разница же здесь в том, что при
|
|||
|
|
исчерпании свободных страниц ситуация будет автоматически исправлена,
|
|||
|
|
посредством записи изменений на диск и формирования новой сильной точки
|
|||
|
|
фиксации.
|
|||
|
|
|
|||
|
|
Таким образом, в режиме безопасной асинхронной фиксации _libmdbx_ будет
|
|||
|
|
всегда использовать новые страницы до исчерпания места в БД или до
|
|||
|
|
явного формирования сильной точки фиксации посредством
|
|||
|
|
`mdbx_env_sync()`. При этом суммарный трафик записи на диск будет
|
|||
|
|
примерно такой же, как если бы отдельно фиксировалась каждая транзакция.
|
|||
|
|
|
|||
|
|
В текущей версии _libmdbx_ вам предоставляется выбор между безопасным
|
|||
|
|
режимом (по умолчанию) асинхронной фиксации, и режимом `UTTERLY_NOSYNC`
|
|||
|
|
когда при системной аварии есть шанс полного разрушения БД как в LMDB.
|
|||
|
|
|
|||
|
|
В последующих версиях _libmdbx_ будут предусмотрены средства для
|
|||
|
|
асинхронной записи данных на диск с автоматическим формированием сильных
|
|||
|
|
точек фиксации.
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
Использование
|
|||
|
|
=============
|
|||
|
|
|
|||
|
|
## Сборка
|
|||
|
|
|
|||
|
|
Для сборки на всех платформах кроме Windows вам потребуются не-дремучие
|
|||
|
|
версии: GNU Make, [bash](https://ru.wikipedia.org/wiki/Bash), компиляторы C и C++ совместимые с GCC или CLANG.
|
|||
|
|
|
|||
|
|
Исторически сборка _libmdbx_ основывается на одном
|
|||
|
|
[Makefile](https://ru.wikipedia.org/wiki/Makefile), что предполагает
|
|||
|
|
разные рецепты сборки в зависимости от целевой платформы. В следующих
|
|||
|
|
версиях планируется переход на использование
|
|||
|
|
[CMake](https://ru.wikipedia.org/wiki/CMake), с отказом от поддержки
|
|||
|
|
других инструментов.
|
|||
|
|
|
|||
|
|
#### Выгрузка DSO/DLL и деструкторы Thread-Local-Storage объектов
|
|||
|
|
При сборке _libmdbx_ в виде разделяемой библиотеки, либо использовании
|
|||
|
|
статической _libmdbx_ в составе другой динамической библиотеке,
|
|||
|
|
желательно убедиться, что ваша система обеспечивает корректность вызова
|
|||
|
|
деструкторов Thread-Local-Storage объектов при выгрузке динамических
|
|||
|
|
библиотек.
|
|||
|
|
|
|||
|
|
Если это не так, то при выгрузке динамической библиотеки с _libmdbx_
|
|||
|
|
внутри возможна либо утечка ресурсов, либо падения из-за вызова
|
|||
|
|
деструкторов из уже выгруженного DSO/DLL объекта. Проблема может
|
|||
|
|
проявляться только в многопоточном приложении, которое производит
|
|||
|
|
выгрузку разделяемых динамических библиотек с кодом _libmdbx_ внутри,
|
|||
|
|
после использования _libmdbx_. Заведомо известно, что TLS-деструкторы
|
|||
|
|
корректно обслуживаются:
|
|||
|
|
|
|||
|
|
- На всех актуальных версиях Windows (Windows 7 и последующих).
|
|||
|
|
|
|||
|
|
- На системах c функцией
|
|||
|
|
[`__cxa_thread_atexit_impl()`](https://sourceware.org/glibc/wiki/Destructor%20support%20for%20thread_local%20variables)
|
|||
|
|
в стандартной библиотеке C. В том числе на системах с GNU libc версии
|
|||
|
|
2.18 и выше.
|
|||
|
|
|
|||
|
|
- На системах с libpthread/ntpl из состава GNU libc с исправлением
|
|||
|
|
ошибок [#21031](https://sourceware.org/bugzilla/show_bug.cgi?id=21031) и
|
|||
|
|
[#21032](https://sourceware.org/bugzilla/show_bug.cgi?id=21032), либо
|
|||
|
|
где нет подобных ошибок в реализации pthreads.
|
|||
|
|
|
|||
|
|
### Linux и другие платформы с GNU Make
|
|||
|
|
Для сборки библиотеки достаточно выполнить `make all` в директории с
|
|||
|
|
исходными текстами, а для выполнения базовых тестов `make check`.
|
|||
|
|
|
|||
|
|
Если установленный в система `make` не является GNU Make, то при попытке
|
|||
|
|
сборки будет масса ошибок от make. В этом случае, возможно, вместо
|
|||
|
|
`make` вам следует использовать `gmake`, либо даже `gnu-make` и т.п.
|
|||
|
|
|
|||
|
|
### FreeBSD и родственные платформы
|
|||
|
|
Как правило, на таких системах по-умолчанию используется Berkeley Make.
|
|||
|
|
А GNU Make вызывается командой `gmake` или может отсутствовать. Кроме
|
|||
|
|
этого может отсутствовать [`bash`](https://ru.wikipedia.org/wiki/Bash).
|
|||
|
|
|
|||
|
|
Вам необходимо установить требуемые компоненты: GNU Make, bash,
|
|||
|
|
компиляторы C и C++ совместимые с GCC или CLANG. После этого для сборки
|
|||
|
|
библиотеки достаточно выполнить `gmake all` (или `make all`) в
|
|||
|
|
директории с исходными текстами, а для выполнения базовых тестов `gmake
|
|||
|
|
check` (или `make check`).
|
|||
|
|
|
|||
|
|
### Windows
|
|||
|
|
Для сборки libmdbx_ для ОС Windows рекомендуется использовать [Microsoft
|
|||
|
|
Visual Studio](https://ru.wikipedia.org/wiki/Microsoft_Visual_Studio),
|
|||
|
|
но не такие инструменты как MinGW, MSYS или Cygwin. Для этого в набор
|
|||
|
|
исходных кодов _libmdbx_ входят соответствующие файлы проектов
|
|||
|
|
совместимые с Visual Studio 2015, Windows SDK для Windows 8.1 и более
|
|||
|
|
поздними версиями. Достаточно открыть `mdbx.sln` и выполнить сборку
|
|||
|
|
библиотеки.
|
|||
|
|
|
|||
|
|
Для сборки с более новыми версиями SDK или Visual Studio должно быть
|
|||
|
|
достаточно выполнить "Retarget solution". Для сборки под старые версии
|
|||
|
|
Windows (например Windows XP) или более старыми компиляторами вам
|
|||
|
|
потребуется самостоятельно преобразовать или воссоздать файлы проектов.
|
|||
|
|
|
|||
|
|
Сборка посредством MinGW, MSYS или Cygwin потенциально возможна. Однако,
|
|||
|
|
эти сценарии не тестируются и вероятно потребуют от вас доработки
|
|||
|
|
`Makefile`. Следует отметить, что в _libmdbx_ предприняты усилия для
|
|||
|
|
устранения runtime зависимостей от CRT и других библиотек Visual Studio.
|
|||
|
|
Для этого достаточно при сборке определить опцию `MDBX_AVOID_CRT`.
|
|||
|
|
|
|||
|
|
Пример запуска базового сценария тестирования можно найти в
|
|||
|
|
[CI-сценарии](appveyor.yml) для [AppVeyor](https://www.appveyor.com/).
|
|||
|
|
Для выполнения [сценария длительного стохастического
|
|||
|
|
тестирования](test/long_stochastic.sh) потребуется
|
|||
|
|
[`bash`](https://ru.wikipedia.org/wiki/Bash), а само тестирование
|
|||
|
|
рекомендуется выполнять с размещением тестовых данных на
|
|||
|
|
[RAM-диске](https://ru.wikipedia.org/wiki/RAM-%D0%B4%D0%B8%D1%81%D0%BA).
|
|||
|
|
|
|||
|
|
### MacOS X
|
|||
|
|
Актуальные [нативные сборочные
|
|||
|
|
инструменты](https://ru.wikipedia.org/wiki/Xcode) для MacOS X включают
|
|||
|
|
GNU Make, CLANG и устаревшую версию bash. Поэтому для сборки библиотеки
|
|||
|
|
достаточно выполнить `make all` в директории с исходными текстами, а для
|
|||
|
|
выполнения базовых тестов `make check`. Если же что-то пойдет не так, то
|
|||
|
|
рекомендуется установить [Homebrew](https://brew.sh/) и попробовать ещё
|
|||
|
|
раз.
|
|||
|
|
|
|||
|
|
Для выполнения [сценария длительного стохастического
|
|||
|
|
тестирования](test/long_stochastic.sh) потребуется установка актуальной
|
|||
|
|
(не устаревшей) версии [`bash`](https://ru.wikipedia.org/wiki/Bash). Для
|
|||
|
|
этого рекомендуется установить [Homebrew](https://brew.sh/), а затем
|
|||
|
|
выполнить `brew install bash`.
|
|||
|
|
|
|||
|
|
## Привязки к другим языкам
|
|||
|
|
|
|||
|
|
| Runtime | GitHub | Author |
|
|||
|
|
| -------- | ------ | ------ |
|
|||
|
|
| Java | [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) |
|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
Сравнение производительности
|
|||
|
|
============================
|
|||
|
|
|
|||
|
|
Все представленные ниже данные получены многократным прогоном тестов на
|
|||
|
|
ноутбуке Lenovo Carbon-2, i7-4600U 2.1 ГГц, 8 Гб ОЗУ, с SSD-диском
|
|||
|
|
SAMSUNG MZNTD512HAGL-000L1 (DXT23L0Q) 512 Гб.
|
|||
|
|
|
|||
|
|
Исходный код бенчмарка [_IOArena_](https://github.com/pmwkaa/ioarena) и
|
|||
|
|
сценарии тестирования [доступны на
|
|||
|
|
github](https://github.com/pmwkaa/ioarena/tree/HL%2B%2B2015).
|
|||
|
|
|
|||
|
|
|
|||
|
|
## Интегральная производительность
|
|||
|
|
|
|||
|
|
Показана соотнесенная сумма ключевых показателей производительности в трёх
|
|||
|
|
бенчмарках:
|
|||
|
|
|
|||
|
|
- Чтение/Поиск на машине с 4-мя процессорами;
|
|||
|
|
|
|||
|
|
- Транзакции с [CRUD](https://ru.wikipedia.org/wiki/CRUD)-операциями
|
|||
|
|
(вставка, чтение, обновление, удаление) в режиме **синхронной фиксации**
|
|||
|
|
данных (fdatasync при завершении каждой транзакции или аналог);
|
|||
|
|
|
|||
|
|
- Транзакции с [CRUD](https://ru.wikipedia.org/wiki/CRUD)-операциями
|
|||
|
|
(вставка, чтение, обновление, удаление) в режиме **отложенной фиксации**
|
|||
|
|
данных (отложенная запись посредством файловой систем или аналог);
|
|||
|
|
|
|||
|
|
*Бенчмарк в режиме асинхронной записи не включен по двум причинам:*
|
|||
|
|
|
|||
|
|
1. Такое сравнение не совсем правомочно, его следует делать с движками
|
|||
|
|
ориентированными на хранение данных в памяти ([Tarantool](https://tarantool.io/), [Redis](https://redis.io/)).
|
|||
|
|
|
|||
|
|
2. Превосходство libmdbx становится еще более подавляющим, что мешает
|
|||
|
|
восприятию информации.
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
## Масштабируемость чтения
|
|||
|
|
|
|||
|
|
Для каждого движка показана суммарная производительность при
|
|||
|
|
одновременном выполнении запросов чтения/поиска в 1-2-4-8 потоков на
|
|||
|
|
машине с 4-мя физическими процессорами.
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
## Синхронная фиксация
|
|||
|
|
|
|||
|
|
- Линейная шкала слева и темные прямоугольники соответствуют количеству
|
|||
|
|
транзакций в секунду, усредненному за всё время теста.
|
|||
|
|
|
|||
|
|
- Логарифмическая шкала справа и желтые интервальные отрезки
|
|||
|
|
соответствуют времени выполнения транзакций. При этом каждый отрезок
|
|||
|
|
показывает минимальное и максимальное время, затраченное на выполнение
|
|||
|
|
транзакций, а крестиком отмечено среднеквадратичное значение.
|
|||
|
|
|
|||
|
|
Выполняется **10.000 транзакций в режиме синхронной фиксации данных** на
|
|||
|
|
диске. При этом требуется гарантия, что при аварийном выключении питания
|
|||
|
|
(или другом подобном сбое) все данные будут консистентны и полностью
|
|||
|
|
соответствовать последней завершенной транзакции. В _libmdbx_ в этом
|
|||
|
|
режиме при фиксации каждой транзакции выполняется системный вызов
|
|||
|
|
[fdatasync](https://linux.die.net/man/2/fdatasync).
|
|||
|
|
|
|||
|
|
В каждой транзакции выполняется комбинированная CRUD-операция (две
|
|||
|
|
вставки, одно чтение, одно обновление, одно удаление). Бенчмарк стартует
|
|||
|
|
на пустой базе, а при завершении, в результате выполняемых действий, в
|
|||
|
|
базе насчитывается 10.000 небольших key-value записей.
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
## Отложенная фиксация
|
|||
|
|
|
|||
|
|
- Линейная шкала слева и темные прямоугольники соответствуют количеству
|
|||
|
|
транзакций в секунду, усредненному за всё время теста.
|
|||
|
|
|
|||
|
|
- Логарифмическая шкала справа и желтые интервальные отрезки
|
|||
|
|
соответствуют времени выполнения транзакций. При этом каждый отрезок
|
|||
|
|
показывает минимальное и максимальное время, затраченное на выполнение
|
|||
|
|
транзакций, а крестиком отмечено среднеквадратичное значение.
|
|||
|
|
|
|||
|
|
Выполняется **100.000 транзакций в режиме отложенной фиксации данных**
|
|||
|
|
на диске. При этом требуется гарантия, что при аварийном выключении
|
|||
|
|
питания (или другом подобном сбое) все данные будут консистентны на
|
|||
|
|
момент завершения одной из транзакций, но допускается потеря изменений
|
|||
|
|
из некоторого количества последних транзакций, что для многих движков
|
|||
|
|
предполагает включение
|
|||
|
|
[WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) (write-ahead
|
|||
|
|
logging) либо журнала транзакций, который в свою очередь опирается на
|
|||
|
|
гарантию упорядоченности данных в журналируемой файловой системе.
|
|||
|
|
_libmdbx_ при этом не ведет WAL, а передает весь контроль файловой
|
|||
|
|
системе и ядру ОС.
|
|||
|
|
|
|||
|
|
В каждой транзакции выполняется комбинированная CRUD-операция (две
|
|||
|
|
вставки, одно чтение, одно обновление, одно удаление). Бенчмарк стартует
|
|||
|
|
на пустой базе, а при завершении, в результате выполняемых действий, в
|
|||
|
|
базе насчитывается 100.000 небольших key-value записей.
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
## Асинхронная фиксация
|
|||
|
|
|
|||
|
|
- Линейная шкала слева и темные прямоугольники соответствуют количеству
|
|||
|
|
транзакций в секунду, усредненному за всё время теста.
|
|||
|
|
|
|||
|
|
- Логарифмическая шкала справа и желтые интервальные отрезки
|
|||
|
|
соответствуют времени выполнения транзакций. При этом каждый отрезок
|
|||
|
|
показывает минимальное и максимальное время, затраченное на выполнение
|
|||
|
|
транзакций, а крестиком отмечено среднеквадратичное значение.
|
|||
|
|
|
|||
|
|
Выполняется **1.000.000 транзакций в режиме асинхронной фиксации
|
|||
|
|
данных** на диске. При этом требуется гарантия, что при аварийном
|
|||
|
|
выключении питания (или другом подобном сбое) все данные будут
|
|||
|
|
консистентны на момент завершения одной из транзакций, но допускается
|
|||
|
|
потеря изменений из значительного количества последних транзакций. Во
|
|||
|
|
всех движках при этом включался режим предполагающий минимальную
|
|||
|
|
нагрузку на диск по записи, и соответственно минимальную гарантию
|
|||
|
|
сохранности данных. В _libmdbx_ при этом используется режим асинхронной
|
|||
|
|
записи измененных страниц на диск посредством ядра ОС и системного
|
|||
|
|
вызова [msync(MS_ASYNC)](https://linux.die.net/man/2/msync).
|
|||
|
|
|
|||
|
|
В каждой транзакции выполняется комбинированная CRUD-операция (две
|
|||
|
|
вставки, одно чтение, одно обновление, одно удаление). Бенчмарк стартует
|
|||
|
|
на пустой базе, а при завершении, в результате выполняемых действий, в
|
|||
|
|
базе насчитывается 10.000 небольших key-value записей.
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
## Потребление ресурсов
|
|||
|
|
|
|||
|
|
Показана соотнесенная сумма использованных ресурсов в ходе бенчмарка в
|
|||
|
|
режиме отложенной фиксации:
|
|||
|
|
|
|||
|
|
- суммарное количество операций ввода-вывода (IOPS), как записи, так и
|
|||
|
|
чтения.
|
|||
|
|
|
|||
|
|
- суммарное затраченное время процессора, как в режиме пользовательских
|
|||
|
|
процессов, так и в режиме ядра ОС.
|
|||
|
|
|
|||
|
|
- использованное место на диске при завершении теста, после закрытия БД
|
|||
|
|
из тестирующего процесса, но без ожидания всех внутренних операций
|
|||
|
|
обслуживания (компактификации LSM и т.п.).
|
|||
|
|
|
|||
|
|
Движок _ForestDB_ был исключен при оформлении результатов, так как
|
|||
|
|
относительно конкурентов многократно превысил потребление каждого из
|
|||
|
|
ресурсов (потратил процессорное время на генерацию IOPS для заполнения
|
|||
|
|
диска), что не позволяло наглядно сравнить показатели остальных движков
|
|||
|
|
на одной диаграмме.
|
|||
|
|
|
|||
|
|
Все данные собирались посредством системного вызова
|
|||
|
|
[getrusage()](http://man7.org/linux/man-pages/man2/getrusage.2.html) и
|
|||
|
|
сканированием директорий с данными.
|
|||
|
|
|
|||
|
|

|
|||
|
|
|
|||
|
|
--------------------------------------------------------------------------------
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
$ 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
|
|||
|
|
start address 0x0000000000003870
|
|||
|
|
|
|||
|
|
Sections:
|
|||
|
|
Idx Name Size VMA LMA File off Algn
|
|||
|
|
11 .text 000173d4 0000000000003870 0000000000003870 00003870 2**4
|
|||
|
|
CONTENTS, ALLOC, LOAD, READONLY, CODE
|
|||
|
|
|
|||
|
|
```
|