背景:
1:任何数据库都不允许数据丢失。
2:事务日志:记录数据库所有变更与行为的历史记录。
3:数据库通过事务日志回放来保证数据库故障恢复后数据不丢失。
概念:
WAL(Wrioter Ahead Logging 预写式日志),是一种规则或协议,指的是将变更和行为写入事务日志的规则。
应用:
DB故障恢复后数据不丢失;
时间点恢复(PITR);
流复制(Streaming Replication, SR)
重要性:
如果没有WAL。当发起insert操作,从数据库集簇文件中加载数据(表或索引)的页面--->到共享内存缓冲池(shared buffer pool)的槽中--->insert语句修改此页面--->此页面没有持久化,为脏页--->再次update更新次页面--->该页面继续被修改--->仍为脏页,没有刷盘---->此时数据库宕机--->数据库恢复后,内存中的修改页面不存在--->数据丢失。
标识 xlog:
xlog: 首部数据和完整元组组成的一对值。
历史修改数据<==>xlog记录(或WAL数据);
lsn 唯一标识一条xlog记录。(xlog记录的lsn标识这个xlog在事务日志中的位置)
xlog的持久化:
Pg将所有历史数据写入持久化存储中。
具体的,当插入、删除、提交变更动作发生时、Pg会把xlog记录写入内存的wal缓冲区。当事务提交或终止,会立即被写入持久存储的WAL段文件。
表lsn与页lsn:
表的lsn是表页面首部pd_lsn数据字段、与页面的LSN是一回事。
重做点:
1:数据库故障恢复从哪一个时间点恢复:重做点(redo 点)
2:重做点: 当前最新的检查点开始时xlog记录写入的(写入wal段文件)位置。(检查点: chekpoint进程启动时,检查点xlog写入wal段文件,检查点xlog记录中包含最新的重做点位置)
xlog持久化:
0:检查点进程启动(这个后台进程是定期执行的),检查点进程负责下面步骤中将xlog记录写入wal段文件的任务。
1:页面加载到共享缓冲池中,当sql语句对数据页面修改,lsn位置创建并记录一条xlog,更新页面lsn.
2:事务提交,wal缓冲区创建并写该提交行为的xlog记录。
3:再将wal缓冲区中所有xlog记录(1, 2的xlog记录?)写入WAL段文件中(进行持久化)。
4:重复1,2,3步骤,所有页面修改,都被当做历史修改记录在wal段文件中
5:数据库故障恢复后,直接wal段文件中恢复。
利用xlog进行数据库恢复(xlog回放):
从redo点开始,依序读取正确的WAL段文件并回放xlog记录。 按照时间顺序。
1:从redo点开始,读取WAL段文件中第一条语句的xlog记录
2:从数据库集簇文件中加载表A的页到内存中的共享缓冲区中
3:回放xlog: 比较当前页面的lsn和xlog的lsn:
(1): xlog lsn > 页 lsn, xlog的数据就会被插入到当前页面中去。
(2): xlog lsn < 页 lsn, 不会做任何事情,直接读取后面wal数据
4:按照时间顺序读取,重复按照3方式回放其他xlog记录
Pg的xlog整页写入机制:
备机:为啥整页写入:当pg_writer进程将脏页刷到磁盘过程中,磁盘上页面损坏,此时xlog无法回放损坏的页面。因此需要完整页面及其首部元信息作为一条xlog记录写入(整页写入)。
包含完整页面的xlog记录称为备份区块或整页镜像。
什么时候写:每个页面在最近的检查带点之后第一次发生变更时,进行一次xlog的整页写入。
xlog整页写入和回放:
1:checkpoint进程启动,(检查点开始)
2:当insert语句导致数据页面发生修改,写一条xlog,这个xlog记录当前的备份区块(即包含了完整的页面)。注意:之所以xlog整页记录,是因为最近的一次检查点之后该页面第一次写入。
3:当这个事务提交,wal缓冲区创建并记录xlog。之后写入WAL段文件。
4:当再来一条insert语句,不再是第一次修改该数据页面,xlog不再整页写入,正常在lsn位置创建并写xlog记录。
5:当这个事务提交,wal缓冲区创建并记录xlog。之后写入WAL段文件。
6:此时数据库发生crash.
7: 等到数据库恢复。
8:从redo点开始,读取WAL段文件中第一条语句的xlog记录。从数据库集簇目录加载表的页面到共享缓冲池。进行回放xlog。
这里的回放规则:
(1): 当第一条xlog是备份区块,回放规则为:xlog记录是一个备份区块时,直接覆盖当前页面,无视页面或xlog记录的lsn,然后将页面的lsn更新为xlog lsn。
(2): xlog lsn > 页 lsn, xlog的数据就会被插入到当前页面中去。
(3): xlog lsn < 页 lsn, 不会做任何事情,直接读取后面wal数据
9:按照时间顺序,重复上诉步骤8。
WAL段文件:
Pg中事务日志默认被分成大小为16MB的文件。这些文件被称为WAL段文件。
事务日志pg_xlog(目录)中包含多个wal段文件。
WAL段文件命名:24个16进制数(一个数占4位)组成。 每个wal段文件大小为16MB。
命名规则:https://blog.csdn.net/m15217321304/article/details/81152555
WAL段文件的内部布局:
段文件大小为16MB,内部划分为多个8KB的页面。
每个8KB的页面:包括多个XLOG record.
XLOG record : 包括首部 Header 和 数据部分 Data
数据部分 Data: 分为两类,备份区块和非备份区块。
WAL记录的写入流程(xlog写入wal段文件的过程)
伪代码流程:
(1)insert into table1 values(1);
(2)exec_simple_query() // 执行sql语句
(3)ExtendClog() // 记录当前事务状态为in_progress, 写入内存的clog中。
(4)heap_insert() // 向共享缓冲池的目标页面上插入元组,创建一条当前页面的xlog记录,调用XLogInsert()。
(5)XlogInsert() // 将插入元组的xlog记录写入到WAL缓冲区LSN_1处,更新被修改页面pd_lsn从LSN_0为LSN_1。
(6)finish_xact_command() // 执行事务提交,创建提交动作的xlog
(7) XLogInsert() //将该提交行为的xlog记录写入到WAL缓冲区LSN_2处
(8)XLogWrite() //将WAL缓冲区所有的xlog记录写入WAL段文件中
(9)TransactionIdCommitTree() // 在Clog中将当前事务的状态由"in_progress"修改为"commit".
(10)以下场景,wal缓冲区的xlog记录都会被写入wal文件
1:运行中的事务提交或终止
2:wal缓冲区被元组xlog记录写满(wal_buffer满了)
3:wal写入进程周期性的执行写入操作。
什么操作会写xlog?
(1) DML
(2) Commit操作,包含提交id的xlog记录
(3) checkpoint进程会写关于该检查点概述信息的xlog记录。检查点xlog记录也会写入WAL缓冲区。
检查点进程(负责脏页刷盘和数据库恢复工作准备):
(1):检查点进程启动时,会将重做点存储在内存中。(这里的重做点是上一次检查点开始时刻XLOG记录的写入位置,也是数据库恢复的开始位置)
(2):该检查点相应的XLOG记录(即检查点xlog记录)会被写入WAL缓冲区。该XLOG记录是由checkpoint结构体定义的,包含一些变量,如(1)中的重做点的位置。
(3):会将共享内存中的所有数据,如CLOG的内容,都会被刷入磁盘中。
(4):共享缓冲池中的所有脏页都会被刷写到存储中。
(5):更新pg_control文件。该文件包含上一次检查点的位置。检查点进程会创建包含重做点的检查点,并将检查点位置与其他信息存储到pg_control文件中。
Pg的重做点是从检查点中获取的。检查点存储在pg_control文件。由检查点进程写入的。
Pg的数据库恢复:
(1):读取pg_control文件中的所有项:(in production 数据库正在运行,进入恢复模式,意味着数据库没有正常停止)
(2):Pg从合适的WAL段文件中读取最近(最新)的检查点记录(检查点相应的xlog),该xlog记录的位置(检查点)写在pg_control文件中。从这个检查点中获取redo点。如果这个最新的检查点是无效的,就会读取前一个检查点。
(3):基于读取的redo点回放xlog记录,直到最新的WAL段文件的最后位置。
(4):具体的回放机制,见上面所讲。
WAL段文件管理(wal的回收利用机制):
1:Pg中xlog记录写入在pg_xlog子目录的WAL段文件中。当旧的段文件写满,就会切换到新的段文件。
2:单检查点进程启动时,pg都会估计并准备一个检查点周期所需的WAL段文件。
估计的准则,是依据当前检查点周期消耗的文件数量。这个文件数量从重做点的段文件开始计数,计数的值在min_wal_size到max_wal_size之间。
3:实例:假设检查点开始之前共有6个wal段文件,wal_3包含上一个检查点周期的redo点。Pg估计需要5个文件,这种情况wal_1-->回收利用为wal_7,wal_2被移除。任何一个比redo点老的段文件理论上都可以被移除。