1. 若是没有WAL,pg挂了会怎么样?
1.1 数据库为了快速缓存,实现了共享内存池,若没有实现wal。

1.2 假设第一次插入语句,PG从磁盘中读取数据到内存里。而后向内存里的某一页插入一个元组。目前是个脏页,由于尚未写入到磁盘(也能够是其余持久化介质)。
1.3 而后又来一个插入语句,到这一页。
1.4 若是因为掉电缘由,操做系统或者PG挂了,数据就没了。
2. 介绍写wal数据和数据库恢复的过程。
2.1 为了处理上文提到的失败的状况,PG引入了WAL。
2.2 XLOG(历史数据、WALdata),当一个操做如插入删除或提交操做产生,WAL段文件会马上写到磁盘中。
2.3 xlog 的LSN记录表示一个位置,这条记录写在事务日志里的位置。每一个xlog是独一无二的。
2.4 Redo 点,是数据库恢复的点。也就是最后一个检查点。事实上,数据恢复和检查点处理密不可分。

3. wal的概述
(1) 检查点创造者是一个后台进程,周期性执行检查点操做。无论何时检查点创造者启动,它就会记录检查点记录到如今的XLog文件。
(2) 假设第一次插入操做,插一条元组到内存页里,写入wal文件。location从LSN_0变为LSN_1。在这个例子中,XLOG是一个头文件和元组实体。
(3) 随着事务提交,PG建立并写了一个Xlog到WALbufer(内存中)而后写到磁盘。LSN变为1
(4) 随着第二次插入操做,PG插入一条新的元组,建立和写入xlog到内存里,并更新表A的LSN变为2。
(5) 当事务提交,PG和步骤3同样。
(6) 假设操做系统失效,全部的内存数据都丢了。
4. pg 恢复的过程

(1) PG读Xlog而后导入到内存里。
(2) PG回放XLOG以前,PG会比较XLOG里的LSN和对应的内存page中的LSN。若是XLOG LSN比页的LSN要大,那么XLOG中的数据会插入到内存页中,而且更新内存页中的LSN。若是LSN比内存页中要小,就读下一个XLOG数据。
(3)PG回放接下来的XLOG。因此redolog 就是xlog。
咱们相信写XLOG会有必定开销,可是比起写整个内存页,咱们能够获得更好的优化。如系统容错。
5 full-page write
假设表A的数据页被损坏了。由于操做系统问题,后台写进程写了脏页进去。Xlog在当前页就没法恢复了,因此须要full-page write。
full-page write默认是启动的,PostgreSQL在每一个检查点以后的每一个页面的第一次更改期间将一对标题数据和整个页面写为XLOG记录;就是说对第一次修改作一个备份。

(1) 检查点进程开始一个检查点进程。
(2) 在插入第一条数据,PG操做会写入整个页由于是第一次操做。
(3) 随着事务提交,page会落盘。
(4) 在插入第二条数据后(先日志后数据)。
(5) 而后PG提交。
(6) 为了证实full-page的做用,咱们考虑磁盘已经坏死,因为操做系统失误。后台写进程已经写到磁盘。
6 数据库恢复经过备份块

(1) PG读取XLOG的日志到内存里,而且是有数据的。
(2) 当一个Xlog记录是全备份,无论他们的lsn是啥,都会覆盖到内存中。
(3) 因为有操做日志信息,能够恢复B。
7事务日志和WAL段文件

WAL段文件的文件名是以下规则

第一个WAL段文件是000000010000000000000001,若是第一个已经被写满了,那么就会写第二个名为000000010000000000000002。xlog名字都是连续的。0000000100000000000000FF写完后,000000010000000100000000是下一个。 规则大概是这样的。
8 wal段文件的内部
一个WAL文件默认是16MB,且内部的页是8k的。 第一个page有头部数据,叫
XLogLongPageHeaderData。其余page头文件叫作
XLogPageHeaderData.

typedef struct XLogPageHeaderData
{
uint16 xlp_magic;
uint16 xlp_info;
TimeLineID xlp_tli;
XLogRecPtr xlp_pageaddr; /* XLOG address of this page */
uint32 xlp_rem_len; /* total len of remaining data for record */
} XLogPageHeaderData;
{
uint16 xlp_magic;
uint16 xlp_info;
TimeLineID xlp_tli;
XLogRecPtr xlp_pageaddr; /* XLOG address of this page */
uint32 xlp_rem_len; /* total len of remaining data for record */
} XLogPageHeaderData;
两个结构差很少,就分析这个。
xlp_magic数字用于正确性校验。由于若是被修改了说明数据不对。
xlp_tli表示该page里第一条数据的时间线。
xlp_pageaddr这个page的内存地址。
xlp_rem_len page的空余大小。
9 XLOG 数据记录的内部
typedef struct XLogRecord
{
uint32 xl_tot_len; /* total len of entire record */ 记录长度
TransactionId xl_xid; /* xact id */ 事务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 */资源管理器ID号
/* 2 bytes of padding here, initialize to zero */
pg_crc32c xl_crc; /* CRC for this record */ crc码
}
{
uint32 xl_tot_len; /* total len of entire record */ 记录长度
TransactionId xl_xid; /* xact id */ 事务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 */资源管理器ID号
/* 2 bytes of padding here, initialize to zero */
pg_crc32c xl_crc; /* CRC for this record */ crc码
}
其中 info +rmid 都用于 资源管理。
PG10.0目前有如下操做
Operation | Resource manager |
---|---|
Heap tuple operations | RM_HEAP, RM_HEAP2 |
Index operations | RM_BTREE, RM_HASH, RM_GIN, RM_GIST, RM_SPGIST, RM_BRIN |
Sequence operations | RM_SEQ |
Transaction operations | RM_XACT, RM_MULTIXACT, RM_CLOG, RM_XLOG, RM_COMMIT_TS |
Tablespace operations | RM_SMGR, RM_DBASE, RM_TBLSPC, RM_RELMAP |
replication and hot standby operations | RM_STANDBY, RM_REPLORIGIN, RM_GENERIC_ID, RM_LOGICALMSG_ID |
2. 若是是更新操做,xl_info会设置成XLOG_HEAP_UPDATE。heap_xlog_update()会重放记录。
3. 当事务提交了,xl_rmid 和xl_info 会设置成RM_XACT和XLOG_XACT_COMMIT。恢复时执行xact_redo_commit();
在9.5之后的版本,xl_len已经被移除了。
10. XLOG记录的数据部分(9.4版本和之前)

主要分为两种备份块和非备份块。
备份块,有两个数据结构和一个数据对象。
1. XLogRecord (头部)2.
BkpBlock 3.
the entire page apart from its free-space
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;
非备份块,一个插入操做会生成两个数据结构,一个数据对象。
1. XLogRecord (头部)2.
xl_heap_insert标记插入的tuple。(属于哪一个表,block位置等)3.数据。
11. XLOG的数据部分(9.5和之后)
9.5之后会划分为头和数据。

头部分会包括0个或多个
XLogRecordBlockHeaders和0个或1个
XLogRecordDataHeaderShort(或XLogRecordDataHeaderLong)。当记录有full-page快照(back up block),XLogRecordBlockHeader 包括
XLogRecordBlockImageHeader若是是压缩的,还包括
XLogRecordBlockCompressHeader。

9.5之后的XLOG 记录
(a)备份块。 1. XLogRecord 头部。 2. XLogRecordBlockHeader等。3. XLogRecordDataHeaderShort 4. 备份块(数据库)5. xl_heap_insert (主要数据)
数据结构主要做用是包含数据,进程号,表ID,偏移量。
非备份块,1. XLogRecord 2. XLogRecordBlockHeader 3. XLogRecordDataHeaderShort 4. an inserted tuple (数据,只有插入的数据没有整个page)5.
xl_heap_insert(偏移量)
最后说一下检查点的构造,1.XLogRecord (头部)2.XLogRecordDataHeaderShort 3.CheckPoint (主要数据)
新的数据结构更适合管理。
XLOG记录的写入
INSERT INTO tbl VALUES ('A');
(1) ExtendCLOG()在内存中CLOG中写状态置为"IN_PROGRESS".
(2) heap_insert() 插入一个堆记录到内存池中,建立XLOG记录,并触发XlogInsert。
(3) XLogInsert()写入XLOG LSN_1而且更新pd_lsn从LSN_1.
(4) finish_xact_command(),提交事务,建立XLOG记录,而后XLogInsert() 写入WAL缓存中在LSN_2。
(5)XLogWrite() 写和刷XLOG从内存中刷到文件里。若是标记为"open_sync"或"open_datasync"就异步。若是是fsync就是同步刷。


写入WAL操做可能会被当即触发当发生如下状况,无论事务是否以及提交:
1. 一个正在跑的事务已经被提交或者被取消了。
2. WAL缓存已经被不少许多写过的元组填满。
3. 一个WAL写常常周期性的写。
理所固然地,DML(数据操做语言)操做写入XLOG记录,但非DML操做也是如此。如上所述,提交操做会写入包含已提交事务的id的XLOG记录。(好比说create alter drop truncate comment rename 也是会写xlog的)另外一个示例能够是用于写入包含该检查点的通常信息的XLOG记录的检查点动做。此外,SELECT语句在特殊状况下建立XLOG记录,但一般不会建立它们。例如,若是在SELECT语句处理期间删除了没必要要的元组而且页面中必要元组的碎片整理由HOT(Heap Only Tuple)发生,则修改页面的XLOG记录将写入WAL缓冲区。(这句话其实我没有很好地理解。)
12. WAL写进程
12. WAL写进程
是一个后台进程,用于按期检查WAL缓冲区并将全部未写入的XLOG记录写入WAL段。此过程的目的是避免突发写入XLOG记录。若是还没有启用此进程,则在一次提交大量数据时,写入XLOG记录可能会遇到瓶颈。该进程是默认工做,且不能被禁止。wal_writer_delay 是间隔,默认是200毫秒。
13 检查点的生成
1. 默认五分钟作一次检查点。能够设置。checkpoint_timeout
2. 在9.4和9.4之前的版本,默认三个段文件作一次检查点。也能够设置。checkpoint_segments
3. 在9.5之后的版本是按照文件大小来算的,默认是1G就是64个文件。
4.在PG开启智能模式或者快速模式下会暂停检查点。
14 检查点处理概述
检查点进程有两个方面:数据库恢复的准备和共享缓冲池上的脏页清除。

(1)检查点过程开始后,REDO点存储在内存中; REDO点是在最新检查点启动时写入XLOG记录的位置,而且是数据库恢复的起点。
(2)该检查点的XLOG记录(即检查点记录)被写入WAL缓冲区。记录的数据部分由结构CheckPoint定义,结构包含几个变量,例如存储在步骤(1)中的REDO点。
(3)全部数据在内存里被刷到底层存储。
(4)全部脏页在共享内存池渐渐地被刷到底层存储。
(5)全部的PG_control文件被更新。这个文件含有基本的包括检查点位置等。
为了从数据库恢复的角度总结上述描述,检查点建立包含REDO点的检查点记录,并将检查点位置和更多内容存储到pg_control文件中。所以,PostgreSQL能够经过从pg_control文件提供的REDO点(从检查点记录得到)重放WAL数据来恢复自身。
15 pg_control file
因为pg_control文件包含检查点的基本信息,所以它对于数据库恢复确定是必不可少的。若是它被破坏或不可读,则恢复过程没法启动以便没法得到起点。
即便pg_control存了超过40个项目,有三个是必须。
1. state :最新检查点开始时数据库服务器的状态。共有七个状态:“start up”是系统启动的状态; 'shutdown'是系统正常关闭命令正常运行的状态; “in production”是系统运行的状态;等等。
2. Latest checkpoint location : LSN最新检查点记录的位置
3. Prior checkpoint location: 上一个LSN的位置,已经被11版本替代了。11版本只存WAL文件,包括了最新的和上一个检查点。
16 数据恢复
PostgreSQL实现了基于重作日志的恢复功能。若是数据库服务器崩溃,PostgreSQL经过从REDO点顺序重放WAL段文件中的XLOG记录来恢复数据库集群。

(1) PostgreSQL在启动时会读取pg_control文件的全部项目。若是状态项处于'in production',PostgreSQL将进入恢复模式,由于这意味着数据库没有正常中止;若是'close',它将进入正常的启动模式。
(2)PostgreSQL从相应的WAL段文件中读取最新的检查点记录,该记录位于pg_control文件中,并从记录中获取REDO点。若是最新的检查点记录无效,PostgreSQL将读取它以前的记录。若是两个记录都不可读,它会自行放弃恢复。
(3)适当的资源管理器从REDO点按顺序读取和重放XLOG记录,直到它们到达最新WAL段的最后一个点。当重放XLOG记录而且它是备份块时,不管其LSN如何,它都将在相应表的页面上被覆盖。不然,仅当此记录的LSN大于相应页面的pd_lsn时,才会重放(非备份块)XLOG记录。

LSN的比较:
1. PG插入一个元组到表A,写一条XLOG在LSN_1的位置。
2. 后台写进程把表A的页刷到存储。此时pd_lsn是1。
3. PG插入一个元组到表A,而且写一个XLOG记录到LSN_2.(先写XLOG文件,数据尚未刷进去)此时马上挂掉,而后启动!

(1) PostgreSQL加载第一个XLOG记录和TABLE_A的页面,但不重放它,由于该记录的LSN不大于TABLE_A的LSN(两个值都是LSN_1)。因此不须要重放。
(2)PostgreSQL重放第二个XLOG记录,由于此记录的LSN(LSN_2)大于当前TABLE_A的LSN(LSN_1)。
今后示例中能够看出,若是非备份块的重放顺序不正确或者屡次重放非备份块,则数据库集群将再也不一致。简而言之,非备份块的重作(重放)操做不是幂等的。所以,为了保留正确的重放顺序,当且仅当其LSN大于相应页面的pd_lsn时,才应重放非备份块记录。
17 WAL段文件管理
PostgreSQL将XLOG记录写入存储在pg_xlog子目录(版本10或更高版本,pg_wal子目录)中的一个WAL段文件中,若是旧文件满了,则切换为新文件。 WAL文件的数量将根据几个配置参数。
WAL 段文件切换(啥时候换下一个文件)
1. WAL文件写满了。
2. pg_switch_xlog函数。
3. archive模式开启,而且有时间限制时,时间触发。
WAL段文件管理(9.5及之后)
每当检查点启动时,PostgreSQL都会估计并准备下一个检查点周期所需的WAL段文件数。这种估计是关于先前检查点周期中消耗的文件数量。而后他会自动删除WAL。

当wal快没的时候就会建立下一个wal文件用于写。当xlog达到max_wal_size,会自动作checkpoint。


WAL段文件管理(9.4及之前)是经过一套公式。
18 持续归档和归档日志
连续存档是一种功能,可在WAL段切换时将WAL段文件复制到存档区域,并由存档(后台)进程执行。复制的文件称为存档日志。此功能一般用于热物理备份和PITR。
拷贝的目录主要是/home/postgres/archives

参数archive_command能够设置任何Unix命令和工具,所以您能够经过设置scp命令或任何文件备份工具而不是普通的复制命令将存档日志传输到其余主机。