21.full page write(全页写)
全页写:
在数据库发生一次 checkpoint 检查点后,需要往数据库的一个数据块里面插入数据,数据库在修改前需要把这个数据块从磁盘读到内存中数据缓冲区 (shared buffer pool) 里,然后再内存中进行数据块的修改插入。当我们执行 insert 语句时,对数据块进行插入数据 A,内存中的数据块里面就会新增一条数据 A。在 commit 提交后,PG 数据块就会将这整个块写到 WAL buffer 日志缓冲区,然后再写到 WAL 日志文件中。然后我们再次对数据块进行插入数据 B,内存中的数据缓冲区同样会再次新增一条数据 B。在 commit 提交后,这个时候 PG 数据库就会将新插入数据 B 的事务日志条目写到 WAL buffer 日志缓冲区,最后再将这条数据库 B 的事务日志条目写到 WAL 日志文件中。
全页写就是把整个数据库块的内容写到 WAL buffer 日志缓冲区和 WAL 日志文件中。一个 WAL 记录长度是 8 字节,每个 WAL 段文件默认为 16MB。一个 WAL 段可以记录将近 200 万事务。而如果存储 8KB 大小的数据块,只能储存 2048 个。就会导致 WAL 的写入量是非常大的。
全页写特点:
-
全页写的概念
将整个数据块写入到 WAL 日志文件中。
-
全页写的优点
提高数据库的安全性,解决块不一致问题。
-
全页写的缺点
导致 WAL 日志膨胀;
增加额外的磁盘 I/O,影响数据库整体性能;
导致主备延迟变大。
-
全页写的控制
full_page_writes(默认 on)。
全页写的模式
1)非强制模式
对于修改操作,当启用全页写时,pg 会在每个检查点之后、每个页面第一次发生变更时,将头数据和整个页面作为一条 WAL 记录写入 WAL 缓冲区。
-
最近一次检查点之后,第一次修改的数据块会进行全页写,后续再修改时不会进行全页写,直到下一次检查点发生。
2)强制模式
对于备份操作,强制启用全页写,只要块发生变化,就会被整块写入 WAL 文件(不管是不是第一次,也不管有没有检查点)。因此,它写入的量是更大的。
-
当用 pg_basebackup 对数据库进行备份时,会自动执行强制模式,在备份期间被修改的数据块会全部写入 WAL 当中。
-
pg_start_backup 命令,对应函数 do_pg_start_backup(xlog.c 文件),其中开启强制全页写。
pg_stop_backup 对应的函数 do_pg_stop_backup,有一句关闭强制全页写。
因此手动执行 pg_start_backup 命令之后,备份完一定要执行 pg_stop_backup,避免 WAL 暴增
建议数据库备份时间点选在业务空闲时间段进行。
块不一致的场景
对 PostgreSQL 来说,块不一致可以发生在两种场景:
-
PG 异常宕机(或者出现磁盘错误)时,数据文件中的页只写入了一部分。
-
使用操作系统命令备份正在运行的数据库,备份途中源数据库可能被修改,此时得到的备份数据状态就是不一致的
无论是崩溃恢复还是备份还原的恢复,都无法基于不一致的数据块进行。
块不一致的原因
-
操作系统进行 I/O 操作时,总是以块为单位,比如 512 字节、1KB 等等。
-
数据库块一般是操作系统块的整数倍,比如 2k、4k、8k 等等。
-
块是数据库最小的 I/O 单位,当数据库写一个数据块时,操作系统需要 I/O 多次,可能在 I/O 过程中系统断电、磁盘故障等等原因导致一个数据块没有完整的写入,导致块不一致。
块不一致恢复
崩溃恢复
-
通过 checksum 发现 “部分写” 的数据页,并将 wal 中保存的这个完整数据页覆盖当前损坏的数据页,然后再继续 redo 恢复整个数据库。
备份恢复
-
restore 阶段,会直接还原不一致的块;但在 recover 阶段,会直接用 WAL 中一致的块对其进行覆盖,然后开始应用日志。
参考:postgresql源码学习(34)—— 事务日志⑩ - 全页写机制_postgresql源码数据页学习-CSDN博客