Внутреннее устройство записи XLOG PostgreSQL
Запись XLOG состоит из заголовка и каждой связанной с ним части данных. В первом подразделе описывается структура заголовка, остальные два подраздела посвящены описанию структуры порции данных в версиях 9.4 и более ранних версий, а также в версии 9.5 (в версии 9.5 формат данных изменился).
Заголовок записи XLOG
Все записи XLOG имеют общую заголовочную часть, определяемую структурой XLogRecord. Ниже показана структура версии 9.4 и более ранних версий, в версии 9.5 она была изменена.
typedef struct XLogRecord
{
uint32 xl_tot_len; /* total len of entire record */
TransactionId xl_xid; /* xact id */
uint32 xl_len; /* total len of rmgr data. This variable was removed in ver.9.5. */
uint8 xl_info; /* flag bits, see below */
RmgrId xl_rmid; /* resource manager for this record */
/* 2 bytes of padding here, initialize to zero */
XLogRecPtr xl_prev; /* ptr to previous record in log */
pg_crc32 xl_crc; /* CRC for this record */
} XLogRecord;
Примечание: Заголовок записи XLOG в версии 9.5 и новее
В версии 9.5 и новее переменная xl_len)была удалена из структуры XLogRecord для оптимизации формата записи XLOG, что уменьшило ее размер на несколько байт.
XLogRecord
typedef struct XLogRecord
{
uint32 xl_tot_len; /* total len of entire record */
TransactionId xl_xid; /* xact id */
XLogRecPtr xl_prev; /* ptr to previous record in log */
uint8 xl_info; /* flag bits, see below */
RmgrId xl_rmid; /* resource manager for this record */
/* 2 bytes of padding here, initialize to zero */
pg_crc32c xl_crc; /* CRC for this record */
/* XLogRecordBlockHeaders and XLogRecordDataHeader follow, no padding */
} XLogRecord;Помимо двух переменных, о которых речь пойдет ниже, большинство переменных настолько очевидны, что не нуждаются в отдельном описании.
И xl_rmid, и xl_info - это переменные, связанные с менеджерами ресурсов, которые представляют собой коллекции операций, связанных с функцией WAL, таких как запись и воспроизведение записей XLOG. Количество менеджеров ресурсов увеличивается с каждой версией PostgreSQL. Версия 10 содержит следующие менеджеры ресурсов:
|
Операция |
Менеджер ресурсов |
|
Операции с котежами |
RM_HEAP, RM_HEAP2 |
|
Операции с индексами |
RM_BTREE, RM_HASH, RM_GIN, RM_GIST, RM_SPGIST, RM_BRIN |
|
Последовательные опеации |
RM_SEQ |
|
Операции с транзакциями |
RM_XACT, RM_MULTIXACT, RM_CLOG, RM_XLOG, RM_COMMIT_TS |
|
Опеации с табличным пространством |
RM_SMGR, RM_DBASE, RM_TBLSPC, RM_RELMAP |
|
Репликация |
RM_STANDBY, RM_REPLORIGIN, RM_GENERIC_ID, RM_LOGICALMSG_ID |
Вот несколько примеров того, как работают менеджеры ресурсов:
- При выполении оператора INSERT переменные заголовка xl_rmid и xl_info XLOG-записи устанавливаются в значения 'RM_HEAP' и 'XLOG_HEAP_INSERT' соответственно. При восстановлении кластера баз данных функция RM_HEAP heap_xlog_insert() выбирается в соответствии с xl_info и воспроизводит эту запись XLOG;
- Аналогично, для оператора UPDATE переменная заголовка xl_info записи XLOG устанавливается в значение 'XLOG_HEAP_UPDATE', функция RM_HEAP heap_xlog_update() воспроизводит запись при восстановлении базы данных;
- Когда транзакция фиксируется, переменные заголовка xl_rmid и xl_info записи XLOG устанавливаются в значения 'RM_XACT' и 'XLOG_XACT_COMMIT' соответственно. При восстановлении кластера базы данных функция xact_redo_commit() воспроизводит эту запись.
Примечание:
Структура XLogRecord в версии 9.4 и более ранних версиях определена в src/include/access/xlog.h , в версиях и новее - в src/include/access/xlogrecord.h.
Функции heap_xlog_insert и heap_xlog_update определены в src/backend/access/heap/heapam.c, а функция xact_redo_commit определена в src/backend/access/transam/xact.c.
Часть данных в рамках записи XLOG (версия 9.4 и более ранние версии)
Часть данных в записи XLOG можно разделить на резервный блок (содержащий всю страницу) и нерезервный блок (содержащий данные в зависимости от выполняемой операции).
Составляющие записи XLOG описаны ниже.
9.4.2.1. Резервный блок
Резервный блок состоит из двух структур данных и одного объекта данных:
- Структура XLogRecord (заголовок);
- Структура BkpBlock;
- Целая страница за исключением свободного пространства.
Структура BkpBlock содержит переменные, идентифицирующие страницу в кластере базы данных (т.е. relfilenode и номер форка отношения, содержащего страницу, и номер блока страницы), а также начальную позицию и длину свободного пространства страницы.
BkpBlock
typedef struct BkpBlock @ include/access/xlog_internal.h
{
RelFileNode node; /* relation containing block */
ForkNumber fork; /* fork within the relation */
BlockNumber block; /* block number */
uint16 hole_offset; /* number of bytes before "hole" */
uint16 hole_length; /* number of bytes in "hole" */
/* ACTUAL BLOCK DATA FOLLOWS AT END OF STRUCT */
} BkpBlock;
Нерезервный блок
В нерезервных блоках расположение частей данных отличается в зависимости от операции. Здесь в качестве показательного примера приводится запись XLOG для оператора INSERT. В этом случае запись XLOG состоит из двух структур данных и одного объекта данных:
- Структура XLogRecord
- Структура xl_heap_insert
- Вставленный кортеж, некотрое количество байтов которого удалено.
Структура xl_heap_insert содержит переменные, идентифицирующие вставленный кортеж в кластере базы данных (т.е. relfilenode таблицы, содержащей этот кортеж, и tid кортежа), а также флаг видимости данного кортежа.
xl_heap_insert
typedef struct BlockIdData
{
uint16 bi_hi;
uint16 bi_lo;
} BlockIdData;
typedef uint16 OffsetNumber;
typedef struct ItemPointerData
{
BlockIdData ip_blkid;
OffsetNumber ip_posid;
}
typedef struct RelFileNode
{
Oid spcNode; /* tablespace */
Oid dbNode; /* database */
Oid relNode; /* relation */
} RelFileNode;
typedef struct xl_heaptid
{
RelFileNode node;
ItemPointerData tid; /* changed tuple id */
} xl_heaptid;
typedef struct xl_heap_insert
{
xl_heaptid target; /* inserted tuple id */
bool all_visible_cleared; /* PD_ALL_VISIBLE was cleared */
} xl_heap_insert;
Примечание:
Причина удаления нескольких байт из вставленного кортежа описана в комментарии к исходному коду структуры xl_heap_header:
Мы не храним в WAL HeapTupleHeaderData вставленного или обновленного кортежа; мы можем сэкономить несколько байт, реконструировав поля, которые доступны в других местах записи WAL, или, возможно, просто не нуждаются в реконструировании.
Примечание:
Структура xl_heap_header определена в src/include/access/htup.h, а структура CheckPoint - в src/include/catalog/pg_control.h.
Данные записи XLOG (версия 9.5 и новее)
В версии 9.4 и более ранних версиях не существовало общего формата записей XLOG, поэтому менеджер ресурсов должен был определять свой собственный формат. Это в большой степени усложняло реализацию функций, связанных с WAL. Чтобы решить эту проблему, в версии 9.5 повился общий структурированный формат, не зависящий от менеджеров ресурсов.
Данные в записи XLOG можно разделить на две части: заголовок и данные.
Заголовок содержит ноль или более XLogRecordBlockHeader, а также ноль или один XLogRecordDataHeaderShort (или XLogRecordDataHeaderLong). Он должен содержать хотя бы один из этих компонентов.
Если запись хранит полностраничное изображение (т. е. резервный блок), XLogRecordBlockHeader включает XLogRecordBlockImageHeader либо XLogRecordBlockCompressHeader, если блок сжат.
XLogRecordBlockHeader
/*
* Header info for block data appended to an XLOG record.
*
* 'data_length' is the length of the rmgr-specific payload data associated
* with this block. It does not include the possible full page image, nor
* XLogRecordBlockHeader struct itself.
*
* Note that we don't attempt to align the XLogRecordBlockHeader struct!
* So, the struct must be copied to aligned local storage before use.
*/
typedef struct XLogRecordBlockHeader
{
uint8 id; /* block reference ID */
uint8 fork_flags; /* fork within the relation, and flags */
uint16 data_length; /* number of payload bytes (not including page
* image) */
/* If BKPBLOCK_HAS_IMAGE, an XLogRecordBlockImageHeader struct follows */
/* If BKPBLOCK_SAME_REL is not set, a RelFileLocator follows */
/* BlockNumber follows */
} XLogRecordBlockHeader;
/*
* The fork number fits in the lower 4 bits in the fork_flags field. The upper
* bits are used for flags.
*/
#define BKPBLOCK_FORK_MASK 0x0F
#define BKPBLOCK_FLAG_MASK 0xF0
#define BKPBLOCK_HAS_IMAGE 0x10 /* block data is an XLogRecordBlockImage */
#define BKPBLOCK_HAS_DATA 0x20
#define BKPBLOCK_WILL_INIT 0x40 /* redo will re-init the page */
#define BKPBLOCK_SAME_REL 0x80 /* RelFileLocator omitted, same as
* previous */
XLogRecordBlockImageHeader
/*
* Additional header information when a full-page image is included
* (i.e. when BKPBLOCK_HAS_IMAGE is set).
*
* The XLOG code is aware that PG data pages usually contain an unused "hole"
* in the middle, which contains only zero bytes. Since we know that the
* "hole" is all zeros, we remove it from the stored data (and it's not counted
* in the XLOG record's CRC, either). Hence, the amount of block data actually
* present is (BLCKSZ - <length of "hole" bytes>).
*
* Additionally, when wal_compression is enabled, we will try to compress full
* page images using one of the supported algorithms, after removing the
* "hole". This can reduce the WAL volume, but at some extra cost of CPU spent
* on the compression during WAL logging. In this case, since the "hole"
* length cannot be calculated by subtracting the number of page image bytes
* from BLCKSZ, basically it needs to be stored as an extra information.
* But when no "hole" exists, we can assume that the "hole" length is zero
* and no such an extra information needs to be stored. Note that
* the original version of page image is stored in WAL instead of the
* compressed one if the number of bytes saved by compression is less than
* the length of extra information. Hence, when a page image is successfully
* compressed, the amount of block data actually present is less than
* BLCKSZ - the length of "hole" bytes - the length of extra information.
*/
typedef struct XLogRecordBlockImageHeader
{
uint16 length; /* number of page image bytes */
uint16 hole_offset; /* number of bytes before "hole" */
uint8 bimg_info; /* flag bits, see below */
/*
* If BKPIMAGE_HAS_HOLE and BKPIMAGE_COMPRESSED(), an
* XLogRecordBlockCompressHeader struct follows.
*/
} XLogRecordBlockImageHeader;
/* Information stored in bimg_info */
#define BKPIMAGE_HAS_HOLE 0x01 /* page image has "hole" */
#define BKPIMAGE_APPLY 0x02 /* page image should be restored
* during replay */
/* compression methods supported */
#define BKPIMAGE_COMPRESS_PGLZ 0x04
#define BKPIMAGE_COMPRESS_LZ4 0x08
#define BKPIMAGE_COMPRESS_ZSTD 0x10
#define BKPIMAGE_COMPRESSED(info) \
((info & (BKPIMAGE_COMPRESS_PGLZ | BKPIMAGE_COMPRESS_LZ4 | \
BKPIMAGE_COMPRESS_ZSTD)) != 0)
XLogRecordBlockCompressHeader
/*
* Extra header information used when page image has "hole" and
* is compressed.
*/
typedef struct XLogRecordBlockCompressHeader
{
uint16 hole_length; /* number of bytes in "hole" */
} XLogRecordBlockCompressHeader;
XLogRecordDataHeader
/*
* XLogRecordDataHeaderShort/Long are used for the "main data" portion of
* the record. If the length of the data is less than 256 bytes, the short
* form is used, with a single byte to hold the length. Otherwise the long
* form is used.
*
* (These structs are currently not used in the code, they are here just for
* documentation purposes).
*/
typedef struct XLogRecordDataHeaderShort
{
uint8 id; /* XLR_BLOCK_ID_DATA_SHORT */
uint8 data_length; /* number of payload bytes */
} XLogRecordDataHeaderShort;
#define SizeOfXLogRecordDataHeaderShort (sizeof(uint8) * 2)
typedef struct XLogRecordDataHeaderLong
{
uint8 id; /* XLR_BLOCK_ID_DATA_LONG */
/* followed by uint32 data_length, unaligned */
} XLogRecordDataHeaderLong;
#define SizeOfXLogRecordDataHeaderLong (sizeof(uint8) + sizeof(uint32))
Примечание: функция сжатия WAL
Функция сжатия WAL внутри PostgreSQL срабатывает (если мы включим ее) при записи полных страниц в WAL, что может сэкономить много накладных расходов на ввод-вывод. Уменьшенный размер сегмента WAL дает дополнительные преимущества при репликации и резервном копировании, поскольку необходимо передавать меньше данных.
В версии 9.5 и новее полностраничные изображения в записях XLOG можно сжимать с помощью метода сжатия LZ, задав параметр wal_compression = enable. В этом случае будет добавлена структура XLogRecordBlockCompressHeader.
Наглядные примеры приведены чуть ниже, как и в предыдущем подразделе.9.4.3.1.
Резервный блок
Резервный блок, созданный оператором INSERT, показан на рис. 106a. Он состоит из четырех структур данных и одного объекта данных:
- Структура the XLogRecord (заголовок);
- Структура XLogRecordBlockHeader, включающая в себя одну структуру LogRecordBlockImageHeader;
- Структура XLogRecordDataHeaderShort;
- Резевный блок;
- Структура the xl_heap_insert.
Структура XLogRecordBlockHeader содержит переменные, необходимые для идентификации блока в кластере базы данных (relfilenode, номер форка и номер блока). Структура XLogRecordImageHeader содержит длину этого блока и номер смещения. (Эти две структуры заголовков вместе могут хранить те же данные, что и структура BkBlock, использовавшаяся до вплоть доверсии 9.4).
В структуре XLogRecordDataHeaderShort хранится длина структуры xl_heap_insert, которая является основными данными записи.
Примечение: основные данные записи XLOG
Основные данные записи XLOG, содержащей полностраничное изображение, как правило, не используются, за исключением некоторых особых случаев, таких как логическое декодирование. Они игнорируются и при повторном воспроизведении записи
Основные данные записей резервных блоков зависят от операторов, которые их создают. Например, оператор UPDATE добавляет xl_heap_lock или xl_heap_updated.
Нерезервный блок
Далее мы поговорим о нерезервном блоке, созданном оператором INSERT (см. рис. 106b). Он состоит из четырех структур данных и одного объекта данных:
- Структура XLogRecord (заголовок);
- Структура XLogRecordBlockHeader;
- Структура XLogRecordDataHeaderShort;
- Вставленный кортеж (точнее структура xl_heap_header);
- Структура xl_heap_insert (основные данные).
Структура XLogRecordBlockHeader содержит три значения (relfilenode, номер форка и номер блока), указывающие на блок, в который был вставлен кортеж, а также длину части данных вставленного кортежа. Структура XLogRecordDataHeaderShort содержит длину новой структуры xl_heap_insert.
Новая структура xl_heap_insert содержит только два значения: номер смещения этого кортежа в блоке и флаг видимости. В структуре XLogRecordBlockHeader хранится большая часть данных, которые содержались в старой структуре xl_heap_insert.
xl_heap_insert
typedef struct xl_heap_insert
{
OffsetNumber offnum; /* inserted tuple's offset */
uint8 flags;
/* xl_heap_header & TUPLE DATA in backup block 0 */
} xl_heap_insert;
В качестве последнего примера на рис. 106c отображена запись контрольной точки, состоящая из трех структур данных:
- Структура XLogRecord (заголовок);
- Структура XLogRecordDataHeaderShort;
- Структура CheckPoint (основные данные).
Примечание:
Структура xl_heap_header определена в src/include/access/htup.h, а структура CheckPoint - в src/include/catalog/pg_control.h.
Хотя новый формат кажется немного сложным, на самом деле, он достаточно хорошо проработаг. Кроме того, размер многих типов записей XLOG обычно меньше, чем у предыдущих. Размер новой контрольной точки больше, чем предыдущей, она содержит больше переменных.






