mirror of
https://github.com/isar/libmdbx.git
synced 2025-01-02 03:34:12 +08:00
mdbx: добавлено описание использования файловых дескрипторов в различных режимах.
This commit is contained in:
parent
559f3005ca
commit
24d7a4d605
107
src/core.c
107
src/core.c
@ -13376,6 +13376,93 @@ __cold int mdbx_env_openW(MDBX_env *env, const wchar_t *pathname,
|
||||
env->me_dbxs[FREE_DBI].md_cmp = cmp_int_align4; /* aligned MDBX_INTEGERKEY */
|
||||
env->me_dbxs[FREE_DBI].md_dcmp = cmp_lenfast;
|
||||
|
||||
/* Использование O_DSYNC или FILE_FLAG_WRITE_THROUGH:
|
||||
*
|
||||
* 0) Если размер страниц БД меньше системной страницы ОЗУ, то ядру ОС
|
||||
* придется чаще обновлять страницы в unified page cache.
|
||||
*
|
||||
* Однако, O_DSYNC не предполагает отключение unified page cache,
|
||||
* поэтому подобные затруднения будем считать проблемой ОС и/или
|
||||
* ожидаемым пенальти из-за использования мелких страниц БД.
|
||||
*
|
||||
* 1) В режиме MDBX_SYNC_DURABLE - O_DSYNC для записи как данных,
|
||||
* так и мета-страниц. Однако, на Linux отказ от O_DSYNC с последующим
|
||||
* fdatasync() может быть выгоднее при использовании HDD, так как
|
||||
* позволяет io-scheduler переупорядочить запись с учетом актуального
|
||||
* расположения файла БД на носителе.
|
||||
*
|
||||
* 2) В режиме MDBX_NOMETASYNC - O_DSYNC можно использовать для данных,
|
||||
* но в этом может не быть смысла, так как fdatasync() всё равно
|
||||
* требуется для гарантии фиксации мета после предыдущей транзакции.
|
||||
*
|
||||
* В итоге на нормальных системах (не Windows) есть два варианта:
|
||||
* - при возможности O_DIRECT и/или io_ring для данных, скорее всего,
|
||||
* есть смысл вызвать fdatasync() перед записью данных, а затем
|
||||
* использовать O_DSYNC;
|
||||
* - не использовать O_DSYNC и вызывать fdatasync() после записи данных.
|
||||
*
|
||||
* На Windows же следует минимизировать использование FlushFileBuffers()
|
||||
* из-за проблем с производительностью. Поэтому на Windows в режиме
|
||||
* MDBX_NOMETASYNC:
|
||||
* - мета обновляется через дескриптор без FILE_FLAG_WRITE_THROUGH;
|
||||
* - перед началом записи данных вызывается FlushFileBuffers(), если
|
||||
* mti_meta_sync_txnid не совпадает с последней записанной мета;
|
||||
* - данные записываются через дескриптор с FILE_FLAG_WRITE_THROUGH.
|
||||
*
|
||||
* 3) В режиме MDBX_SAFE_NOSYNC - O_DSYNC нет смысла использовать, пока не
|
||||
* будет реализована возможность полностью асинхронной "догоняющей"
|
||||
* записи в выделенном процессе-сервере с io-ring очередями внутри.
|
||||
*
|
||||
* -----
|
||||
*
|
||||
* Использование O_DIRECT или FILE_FLAG_NO_BUFFERING:
|
||||
*
|
||||
* Назначение этих флагов в отключении файлового дескриптора от
|
||||
* unified page cache, т.е. от отображенных в память данных в случае
|
||||
* libmdbx.
|
||||
*
|
||||
* Поэтому, использование direct i/o в libmdbx без MDBX_WRITEMAP лишено
|
||||
* смысла и контр-продуктивно, ибо так мы провоцируем ядро ОС на
|
||||
* не-когерентность отображения в память с содержимым файла на носителе,
|
||||
* либо требуем дополнительных проверок и действий направленных на
|
||||
* фактическое отключение O_DIRECT для отображенных в память данных.
|
||||
*
|
||||
* В режиме MDBX_WRITEMAP когерентность отображенных данных обеспечивается
|
||||
* физически. Поэтому использование direct i/o может иметь смысл, если у
|
||||
* ядра ОС есть какие-то проблемы с msync(), в том числе с
|
||||
* производительностью:
|
||||
* - использование io_ring или gather-write может быть дешевле, чем
|
||||
* просмотр PTE ядром и запись измененных/грязных;
|
||||
* - но проблема в том, что записываемые из user mode страницы либо не
|
||||
* будут помечены чистыми (и соответственно будут записаны ядром
|
||||
* еще раз), либо ядру необходимо искать и чистить PTE при получении
|
||||
* запроса на запись.
|
||||
*
|
||||
* Поэтому O_DIRECT или FILE_FLAG_NO_BUFFERING используется:
|
||||
* - только в режиме MDBX_SYNC_DURABLE с MDBX_WRITEMAP;
|
||||
* - когда me_psize >= me_os_psize;
|
||||
* - опция сборки MDBX_AVOID_MSYNC != 0, которая по-умолчанию включена
|
||||
* только на Windows (см ниже).
|
||||
*
|
||||
* -----
|
||||
*
|
||||
* Использование FILE_FLAG_OVERLAPPED на Windows:
|
||||
*
|
||||
* У Windows очень плохо с I/O (за исключением прямых постраничных
|
||||
* scatter/gather, которые работают в обход проблемного unified page
|
||||
* cache и поэтому почти бесполезны в libmdbx).
|
||||
*
|
||||
* При этом всё еще хуже при использовании FlushFileBuffers(), что также
|
||||
* требуется после FlushViewOfFile() в режиме MDBX_WRITEMAP. Поэтому
|
||||
* на Windows вместо FlushViewOfFile() и FlushFileBuffers() следует
|
||||
* использовать запись через дескриптор с FILE_FLAG_WRITE_THROUGH.
|
||||
*
|
||||
* В свою очередь, запись с FILE_FLAG_WRITE_THROUGH дешевле/быстрее
|
||||
* при использовании FILE_FLAG_OVERLAPPED. В результате, на Windows
|
||||
* в durable-режимах запись данных всегда в overlapped-режиме,
|
||||
* при этом для записи мета требуется отдельный не-overlapped дескриптор.
|
||||
*/
|
||||
|
||||
rc = osal_openfile((flags & MDBX_RDONLY) ? MDBX_OPEN_DXB_READ
|
||||
: MDBX_OPEN_DXB_LAZY,
|
||||
env, env_pathname.dxb, &env->me_lazy_fd, mode);
|
||||
@ -13401,6 +13488,26 @@ __cold int mdbx_env_openW(MDBX_env *env, const wchar_t *pathname,
|
||||
if ((flags & (MDBX_RDONLY | MDBX_SAFE_NOSYNC)) == MDBX_SYNC_DURABLE) {
|
||||
ior_flags = IOR_OVERLAPPED;
|
||||
if ((flags & MDBX_WRITEMAP) && MDBX_AVOID_MSYNC) {
|
||||
/* Запрошен режим MDBX_SAFE_NOSYNC | MDBX_WRITEMAP при активной опции
|
||||
* MDBX_AVOID_MSYNC.
|
||||
*
|
||||
* 1) В этой комбинации наиболее выгодно использовать WriteFileGather(),
|
||||
* но для этого необходимо открыть файл с флагом FILE_FLAG_NO_BUFFERING и
|
||||
* после обеспечивать выравнивание адресов и размера данных на границу
|
||||
* системной страницы, что в свою очередь возможно если размер страницы БД
|
||||
* не меньше размера системной страницы ОЗУ. Поэтому для открытия файла в
|
||||
* нужном режиме требуется знать размер страницы БД.
|
||||
*
|
||||
* 2) Кроме этого, в Windows запись в заблокированный регион файла
|
||||
* возможно только через тот-же дескриптор. Поэтому изначальный захват
|
||||
* блокировок посредством osal_lck_seize(), захват/освобождение блокировок
|
||||
* во время пишущих транзакций и запись данных должны выполнять через один
|
||||
* дескриптор.
|
||||
*
|
||||
* Таким образом, требуется прочитать волатильный заголовок БД, чтобы
|
||||
* узнать размер страницы, чтобы открыть дескриптор файла в режиме нужном
|
||||
* для записи данных, чтобы использовать именно этот дескриптор для
|
||||
* изначального захвата блокировок. */
|
||||
MDBX_meta header;
|
||||
if (read_header(env, &header, MDBX_SUCCESS, true) == MDBX_SUCCESS &&
|
||||
header.mm_psize >= env->me_os_psize)
|
||||
|
Loading…
x
Reference in New Issue
Block a user