mdbx: update README (add perfomance comparison).

Change-Id: I0cea926d37b83dbe787b72031ecae28095d98160
This commit is contained in:
Leo Yuriev 2017-07-04 12:07:05 +03:00
parent aec35300c4
commit 36ef355332

223
README.md
View File

@ -16,14 +16,15 @@ and [by Yandex](https://translate.yandex.ru/translate?url=https%3A%2F%2Fgithub.c
_libmdbx_ - это встраиваемый key-value движок хранения со специфическим
набором возможностей, которые при правильном применении позволяют
создавать уникальные решения с чемпионской производительностью, идеально
сочетаясь с технологией [MRAM](https://en.wikipedia.org/wiki/Magnetoresistive_random-access_memory).
сочетаясь с технологией
[MRAM](https://en.wikipedia.org/wiki/Magnetoresistive_random-access_memory).
_libmdbx_ обновляет совместно используемый набор данных, никак не мешая
при этом параллельным операциям чтения, не применяя атомарных операций к
самим данным, и обеспечивая согласованность при аварийной остановке в
любой момент. Поэтому _libmdbx_ позволяя строить системы с линейным
масштабированием производительности чтения/поиска по ядрам CPU и
амортизационной стоимостью любых операций Olog(N).
_libmdbx_ умеет обновлять совместно используемый набор данных, никак не
мешая при этом параллельным операциям чтения, не применяя атомарных
операций к самим данным, и обеспечивая согласованность при аварийной
остановке в любой момент. Поэтому _libmdbx_ позволяя строить системы с
линейным масштабированием производительности чтения/поиска по ядрам CPU
и амортизационной стоимостью любых операций Olog(N).
### История
@ -37,6 +38,12 @@ _libmdbx_ является потомком "Lightning Memory-Mapped Database",
[представлен на конференции 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).
Характеристики и ключевые особенности
=====================================
@ -45,14 +52,12 @@ _libmdbx_ наследует все ключевые возможности и
своего прародителя [LMDB](https://en.wikipedia.org/wiki/Lightning_Memory-Mapped_Database),
с устранением описанных далее проблем и архитектурных недочетов.
### Общее для оригинальной _LMDB_ и _libmdbx_
1. Данные хранятся в упорядоченном отображении (ordered map), ключи всегда
отсортированы, поддерживается выборка диапазонов (range lookups).
2. Данные отображается в память каждого работающего с БД процесса.
Ключам и данным обеспечивается прямой доступ без необходимости их
копирования, так как они защищены транзакцией чтения и не изменяются.
К данным и ключам обеспечивается прямой доступ без необходимости их
копирования.
3. Транзакции согласно
[ACID](https://ru.wikipedia.org/wiki/ACID), посредством
@ -71,10 +76,10 @@ _libmdbx_ наследует все ключевые возможности и
значениями), без дублирования ключей, с сортировкой значений, в
том числе целочисленных (для вторичных индексов).
6. Эффективная поддержка ключей фиксированной длины, в том числе целочисленных.
6. Эффективная поддержка коротких ключей фиксированной длины, в том числе целочисленных.
7. Амортизационная стоимость любой операции Olog(N),
[WAF](https://en.wikipedia.org/wiki/Write_amplification) и RAF также 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) и журнала
транзакций, после сбоев не требуется восстановление. Не требуется компактификация
@ -82,10 +87,151 @@ _libmdbx_ наследует все ключевые возможности и
"по горячему", на работающей БД без приостановки изменения данных.
9. Отсутствует какое-либо внутреннее управление памятью или кэшированием. Всё
необходимое штатно выполняет ядро ОС.
необходимое штатно выполняет ядро ОС!
### Недостатки и Компромиссы
Сравнение производительности
============================
Все данные получены многократным прогоном тестов на ноутбуке 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).
--------------------------------------------------------------------------------
### Интегральная производительность
![Comparison #1: Integral Performance](https://raw.githubusercontent.com/wiki/ReOpen/libmdbx/img/perf-slide-1.png)
Показана соотнесенная сумма показателей производительности в трёх
бенчмарках:
- Чтение/Поиск на машине с 4-мя процессорами;
- Транзакции с [CRUD](https://ru.wikipedia.org/wiki/CRUD)-операциями
(вставка, чтение, обновление, удаление) в режиме **синхронной фиксации**
данных (fdatasync при завершении каждой транзакции или аналог);
- Транзакции с [CRUD](https://ru.wikipedia.org/wiki/CRUD)-операциями
(вставка, чтение, обновление, удаление) в режиме **отложенной фиксации**
данных (отложенная запись посредством файловой систем или аналог);
*Бенчмарк в режиме асинхронной записи не включен по двум причинам:*
1. Такое сравнение не совсем правомочно, его следует делать с движками
ориентированными на хранение данных в памяти (Tarantool, Redis).
2. Превосходство libmdbx становится еще более подавляющем, что мешает
восприятию информации.
--------------------------------------------------------------------------------
### Масштабируемость чтения
![Comparison #2: Read Scalability](https://raw.githubusercontent.com/wiki/ReOpen/libmdbx/img/perf-slide-2.png)
Для каждого движка показана суммарная производительность при
одновременном выполнении запросов чтения/поиска в 1-2-4-8 потоков на
машине с 4-мя процессорами.
--------------------------------------------------------------------------------
### Синхронная фиксация
![Comparison #3: Sync-write mode](https://raw.githubusercontent.com/wiki/ReOpen/libmdbx/img/perf-slide-3.png)
- Линейная шкала слева и темные прямоугольники соответствуют количеству
транзакций в секунду, усредненному за все время теста.
- Логарифмическая шкала справа и желтые интервальные отрезки
соответствуют времени выполнения транзакций. При этом каждый отрезок
показывает минимальное и максимальное время затраченной на выполнения
транзакций, а крестик показывает среднеквадратичное значение.
Выполняется **10.000 транзакций в режиме синхронной фиксации данных** на
диске. При этом требуется гарантия, что при аварийном выключении питания
(или другом подобном сбое) все данные будут консистентны и полностью
соответствовать последней завершенной транзакции. В _libmdbx_ в этом
режиме при фиксации каждой транзакции выполняется системный вызов
[fdatasync](https://linux.die.net/man/2/fdatasync).
В каждой транзакции выполняется CRUD-операция (две вставки, одной
чтение, одно обновление, одно удаление). Бенчмарк стартует на пустой
базе и в результате выполняемых действий при завершении в базе
насчитывается 10.000 небольших key-value записей.
--------------------------------------------------------------------------------
### Отложенная фиксация
![Comparison #4: Lazy-write mode](https://raw.githubusercontent.com/wiki/ReOpen/libmdbx/img/perf-slide-4.png)
- Линейная шкала слева и темные прямоугольники соответствуют количеству транзакций в секунду, усредненному за все время теста.
- Логарифмическая шкала справа и желтые интервальные отрезки соответствуют времени выполнения транзакций. При этом каждый отрезок показывает минимальное и максимальное время затраченной на выполнения транзакций, а крестик показывает среднеквадратичное значение.
Выполняется **100.000 транзакций в режиме отложенной фиксации данных** на диске. При этом требуется гарантия, что при аварийном выключении питания (или другом подобном сбое) все данные будут консистентны на момент завершения одной из транзакций, но допускается потеря изменений из некоторого количества последних транзакций, что для многих движков предполагает включение [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging) (write-ahead logging) либо журнала транзакций, который в свою очередь опирается на гарантию упорядоченности данных в журналируемой файловой системе. _libmdbx_ при этом не ведет WAL, а передает весь контроль файловой системе и ядру ОС.
В каждой транзакции выполняется CRUD-операция (две вставки, одной чтение, одно обновление, одно удаление).
Бенчмарк стартует на пустой базе и в результате выполняемых действий при завершении в базе насчитывается 100.000 небольших key-value записей.
--------------------------------------------------------------------------------
### Асинхронная фиксация
![Comparison #5: Async-write mode](https://raw.githubusercontent.com/wiki/ReOpen/libmdbx/img/perf-slide-5.png)
- Линейная шкала слева и темные прямоугольники соответствуют количеству
транзакций в секунду, усредненному за все время теста.
- Логарифмическая шкала справа и желтые интервальные отрезки
соответствуют времени выполнения транзакций. При этом каждый отрезок
показывает минимальное и максимальное время затраченной на выполнения
транзакций, а крестик показывает среднеквадратичное значение.
Выполняется **1.000.000 транзакций в режиме асинхронной фиксации
данных** на диске. При этом требуется гарантия, что при аварийном
выключении питания (или другом подобном сбое) все данные будут
консистентны на момент завершения одной из транзакций, но допускается
потеря изменений из любого количества последних транзакций. Во всех
движках при этом включался режим предполагающий минимальную нагрузку
записи на диск, и соответственно минимальную гарантию сохранности
данных. В _libmdbx_ при этом используется режим асинхронной записи
измененных страниц на диск силами ядра ОС посредством системрго вызова
[msync(MS_ASYNC)](https://linux.die.net/man/2/msync).
В каждой транзакции выполняется CRUD-операция (две вставки, одной
чтение, одно обновление, одно удаление). Бенчмарк стартует на пустой
базе и в результате выполняемых действий при завершении в базе
насчитывается 1.000.000 небольших key-value записей.
--------------------------------------------------------------------------------
### Стоимость как потребление ресурсов
![Comparison #6: Cost comparison](https://raw.githubusercontent.com/wiki/ReOpen/libmdbx/img/perf-slide-6.png)
Показана соотнесенная сумма использованных ресурсов в ходе бенчмарка в
режиме отложенной фиксации:
- суммарное количество операций ввода-вывода (IOPS), как записи, так и
чтения.
- суммарное затраченное время процессора, как в режиме пользователя,
так и в режиме ядра ОС.
- максимальный объем места на диске. который требовался во время работы
теста.
Движок _ForestDB_ был исключен при окончательном формировании диаграммы,
так как многократно превысил потребление каждого из ресурсов (потратил
процессорное время на генерацию IOPS для заполнения диска). Что не
позволяло наглядно сравнить показатели остальных движков на одной
диаграмме.
Все данные собирались посредством системного вывова
[getrusage()](http://man7.org/linux/man-pages/man2/getrusage.2.html) и
сканированием директорий с данными.
--------------------------------------------------------------------------------
## Недостатки и Компромиссы
1. Единовременно может выполняться не более одной транзакция изменения данных
(один писатель). Зато все изменения всегда последовательны, не может быть
@ -93,20 +239,22 @@ _libmdbx_ наследует все ключевые возможности и
2. Отсутствие [WAL](https://en.wikipedia.org/wiki/Write-ahead_logging)
обуславливает относительно большой
[WAF](https://en.wikipedia.org/wiki/Write_amplification). Поэтому фиксация
изменений на диске может быть дорогой и является главным ограничителем для
производительности по записи. В качестве компромисса предлагается несколько
режимов ленивой и/или периодической фиксации. В том числе режим `MAPASYNC`,
при котором изменения происходят только в памяти и асинхронно фиксируются на
диске ядром ОС.
[WAF](https://en.wikipedia.org/wiki/Write_amplification) (Write
Amplification Factor). Поэтому фиксация изменений на диске может быть
дорогой и является главным ограничителем для производительности по
записи. В качестве компромисса предлагается несколько режимов ленивой
и/или периодической фиксации. В том числе режим `MAPASYNC`, при котором
изменения происходят только в памяти и асинхронно фиксируются на диске
ядром ОС.
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`.
уровне страниц в [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`.
4. В _LMDB_ существует проблема долгих чтений (приостановленных читателей),
которая приводит к деградации производительности и переполнению БД.
@ -121,6 +269,12 @@ _libmdbx_ наследует все ключевые возможности и
#### Проблема долгих чтений
*Следует отметить*, что проблема "сборки мусора" так или иначе
существует во всех СУБД (Vacuum в PostgreSQL). Однако в случае _libmdbx_
и LMDB она проявляется более остро, прежде всего из-за высокой
производительности, а также из-за намеренного прощения внутренних
механизмов ради производительности.
Понимание проблемы требует некоторых пояснений, которые
изложены ниже, но могут быть сложны для быстрого восприятия.
Поэтому, тезисно:
@ -202,9 +356,10 @@ RECLAIM` в _libmdbx_.
В _libmdbx_ эта проблема устранена, подробности ниже.
--------------------------------------------------------------------------------
Доработки _libmdbx_
===================
Дополнительные "фичи" _libmdbx_ относительно LMDB
=================================================
1. Режим `LIFO RECLAIM`.
@ -377,3 +532,15 @@ RECLAIM` в _libmdbx_.
закрываются и не теряются при завершении таких транзакций посредством
mdbx_txn_abort() или mdbx_txn_reset(). Что позволяет избавится от ряда
сложно обнаруживаемых ошибок.
26. Генерация последовательностей посредством `mdbx_dbi_sequence()`.
27. Обновление данных с одновременным получением старых значений,
а также адресное изменение конкретного multi-значения посредством `mdbx_replace()`.
28. Расширенное динамическое управление размером БД, включая выбор размера страницы
посредством `mdbx_env_set_geometry()`.
29. Три мета-страницы вместо двух, что позволяет гарантированно консистентно
обновлять слабые контрольные точки фиксации без риска повредить крайнюю сильную
точку фиксации.