Форматы файлов
Для обеспечения сохранности данных
Tarantool записывает каждый запрос на изменение данных (insert, update,
delete, replace, upsert) в файл журнала предзаписи (WAL) в каталоге
wal.dir. Каждому запросу на изменение
данных присваивается непрерывно возрастающий 64-битный порядковый номер
в журнале. Имя файла WAL основано на порядковом номере в журнале для
первой записи в файле, к которому добавляется расширение .xlog. Новый
файл WAL создаётся, когда текущий достигает размера
wal_max_size.
Каждая запись WAL содержит:
- порядковый номер в журнале
- запрос на изменение данных (формат соответствует бинарному протоколу Tarantool)
- заголовок
- метаданные
- данные, отформатированные по правилам msgpack
Чтобы просмотреть шестнадцатеричные байты заданного файла WAL, используйте
команду hexdump:
$ hexdump 00000000000000000000.xlog
Например, файл WAL после первого запроса INSERT может выглядеть следующим образом:
Hex dump of WAL file Comment-------------------- -------58 4c 4f 47 0a "XLOG\n"30 2e 31 33 0a "0.13\n" = version53 65 72 76 65 72 3a 20 "Server: "38 62 66 32 32 33 65 30 2d [Server UUID]\n36 39 31 34 2d 34 62 35 352d 39 34 64 32 2d 64 32 6236 64 30 39 62 30 31 39 360a56 43 6c 6f 63 6b 3a 20 "Vclock: "7b 7d "{}" = vclock value, initially blank... (not shown = tuples for system spaces)d5 ba 0b ab Magic row marker always = 0xab0bbad519 Length, not including length of header, = 25 bytes00 Record header: previous crc32ce 8c 3e d6 70 Record header: current crc32a7 cc 73 7f 00 00 66 39 Record header: padding84 msgpack code meaning "Map of 4 elements" follows00 02 element#1: tag=request type, value=0x02=IPROTO_INSERT02 01 element#2: tag=server id, value=0x0103 04 element#3: tag=lsn, value=0x0404 cb 41 d4 e2 2f 62 fd d5 d4 element#4: tag=timestamp, value=an 8-byte "Float64"82 msgpack code meaning "map of 2 elements" follows10 cd 02 00 element#1: tag=space id, value=512, big byte first21 91 01 element#2: tag=tuple, value=1-element fixed array={1}
Tarantool обрабатывает запросы атомарно: изменение либо принимается и записывается в WAL, либо полностью отклоняется. Чтобы прояснить, как это происходит, см. пример с запросом REPLACE ниже:
- Экземпляр сервера пытается найти исходный кортеж по первичному ключу. Если кортеж найден, ссылка на него сохраняется для последующего использования.
- Выполняется проверка нового кортежа. Если, например, он не содержит индексированного поля или содержит индексированное поле, тип которого не соответствует типу, заданному в определении индекса, изменение отклоняется.
- Новый кортеж заменяет старый во всех существующих индексах.
- В отдельный поток записи WAL отправляется сообщение с запросом на запись изменения в WAL. Экземпляр переходит к обработке следующего запроса до получения подтверждения записи.
- При успешном выполнении клиенту отправляется подтверждение. При
неудаче запускается процедура отката. Во время процедуры отката
обработчик транзакций откатывает все изменения в базе данных,
произошедшие после первого неудачного изменения – от самых поздних
к самым ранним, вплоть до первого неудачного изменения. Все
откаченные запросы прерываются с ошибкой
ER_WAL_IO. Пока выполняется откат, новые изменения не применяются. После завершения процедуры отката сервер перезапускает конвейер обработки.
Преимущество описанного алгоритма – полная конвейеризация запросов, даже для запросов с одинаковым значением первичного ключа. В результате производительность базы данных не снижается, даже если все запросы обращаются к одному и тому же ключу в одном и том же спейсе.
Поток обработки транзакций обменивается сообщениями с потоком записи WAL в асинхронном (но надёжном) режиме. Поток обработки транзакций, не блокируясь на задачах WAL, продолжает быстро обрабатывать запросы даже при высоком объёме дисковых операций ввода-вывода. Ответ на запрос отправляется сразу после готовности, даже если на том же соединении есть более ранние незавершённые запросы. В частности, производительность SELECT, даже для SELECT, выполняемых на соединении, заполненном запросами UPDATE и DELETE, остаётся независимой от нагрузки на диск.
При записи WAL используется несколько режимов надёжности, определяемых
конфигурационным параметром
wal.mode. Журнал предзаписи можно
полностью отключить, задав для параметра wal_mode значение none.
Даже без журнала предзаписи остаётся возможность создать постоянную
копию всего набора данных с помощью запроса
box.snapshot().
Файл .xlog всегда содержит изменения, основанные на первичном ключе.
Даже если клиент запросил обновление или удаление с использованием
вторичного ключа, запись в файле .xlog содержит первичный ключ.
Формат файла снимка (.snap) следующий:
- Заголовок снимка содержит глобальный уникальный идентификатор экземпляра и позицию файла снимка в истории относительно более ранних файлов снимков.
- Содержимое снимка содержит записи вставок в спейсы memtx. Это отличается
от содержимого файла
.xlog, который может содержать записи для любых запросов на изменение данных (вставки, обновления, upsert и удаления).
В первую очередь, записи в файле снимка упорядочены следующим образом:
- Системные спейсы (id >= 256 && id <= 511), упорядоченные по ID.
- Несистемные спейсы, упорядоченные по ID.
Во вторую очередь, записи в файле .snap упорядочены по первичному ключу в пределах каждого спейса.
Заголовок файла .snap или .xlog может выглядеть следующим образом:
<type>\n SNAP\n or XLOG\n<version>\n currently 0.13\nServer: <server_uuid>\n where UUID is a 36-byte stringVClock: <vclock_map>\n e.g. {1: 0}\n\n
После заголовка файла следуют кортежи данных. Кортежи начинаются с
маркера строки 0xd5ba0bab, а за последним кортежем может следовать
маркер конца файла 0xd510aded. Таким образом, между заголовком файла и
маркером конца файла могут находиться кортежи данных в следующем
формате:
0 3 4 17<table class="tableblock frame-all grid-all stretch"><colgroup><col style="width: 20%;"><col style="width: 20%;"><col style="width: 20%;"><col style="width: 20%;"><col style="width: 20%;"></colgroup><thead><tr><th class="tableblock halign-left valign-top">{decodeBase64(IDB4ZDViYTBiYWI=)}</th><th class="tableblock halign-left valign-top">{decodeBase64(IExFTkdUSA==)}</th><th class="tableblock halign-left valign-top">{decodeBase64(IENSQzMyIFBSRVY=)}</th><th class="tableblock halign-left valign-top">{decodeBase64(IENSQzMyIENVUg==)}</th><th class="tableblock halign-left valign-top">{decodeBase64(IFBBRERJTkc=)}</th></tr></thead></table>MP_FIXEXT2 MP_INT MP_INT MP_INT ---+============+ +===================================+| | | || HEADER | | BODY || | | |+============+ +===================================+MP_MAP MP_MAP