PgSQL · 特性分析 · full page write 机制

简介: PG默认每个page的大小为8K,PG数据页写入是以page为单位,但是在断电等情况下,操作系统往往不能保证单个page原子地写入磁盘,这样就极有可能导致部分数据块只写到4K(操作系统是一般以4K为单位),这些“部分写”的页面包含新旧数据的混合。在崩溃后的恢复期间,xlog 里面存储的记录变化信息.

PG默认每个page的大小为8K,PG数据页写入是以page为单位,但是在断电等情况下,操作系统往往不能保证单个page原子地写入磁盘,这样就极有可能导致部分数据块只写到4K(操作系统是一般以4K为单位),这些“部分写”的页面包含新旧数据的混合。在崩溃后的恢复期间,xlog 里面存储的记录变化信息不够完整,无法完全恢复该页。PG为了解决这类问题,full_page_write机制孕育而生。

什么是full_page_write?

PostgreSQL 在 checkpoint 之后在对数据页面的第一次写的时候会将整个数据页面写到 xlog 里面。当出现主机断电或者OS崩溃时,redo操作时通过checksum发现“部分写”的数据页,并将xlog中保存的这个完整数据页覆盖当前损坏的数据页,然后再继续redo就可以恢复整个数据库了。
除了能够解决断电等带来坏数据页问题外,full_page_write 还应用在在线备份功能上。PG进行全量备份数据库一般通过pg_basebackup工具实现,pg_basebackup类似于copy操作,在此期间,也会出现部分数据页写到一半时文件被copy走了,正是因为full_page_write存在,备份出来的数据库才可以成功恢复启动。所以即便full_page_write=off,在备份时也会被强制自动打开,保证备份成功。

实现原理

full_page_write主要在XLogInsert(插入一条xlog记录)时发挥作用,通过full_page_writer开关状态以及是否是checkpoint后对数据页面的第一次修改(lsn<RedoRecPtr)判断是否需要备份数据页。如果需要备份,那么则把数据页存放在这条记录的末尾,最终写入到xlog中。

doPageWrites = Insert->fullPageWrites || Insert->forcePageWrites;
/* Find info for buffer */
for (i = 0; i < XLR_MAX_BKP_BLOCKS; i++)
{
    if (dtbuf[i] == InvalidBuffer)
    {
    	/* OK, put it in this slot */
    	dtbuf[i] = rdt->buffer;
    	if (doPageWrites && XLogCheckBuffer(rdt, true,&(dtbuf_lsn[i]), &(dtbuf_xlg[i])))
    	{
    		dtbuf_bkp[i] = true;
    		rdt->data = NULL;
    		rdt->len = 0;
    	}
    	break;
    }
    ......
}

在redo恢复的时候只要数据块有备份,那么就是用备份的数据。

/* If we have a full-page image, restore it and we're done */
if (record->xl_info & XLR_BKP_BLOCK(0))
{
	(void) RestoreBackupBlock(lsn, record, 0, false, false);
	return;
}

fullpagewrite

full_page_write不足之处

因为full_page_write需要在xlog中记录数据页,会写更多xlog文件,不仅有数据变化信息,还有数据页本身信息,这样会增加额外的IO和磁盘消耗,同时也会引起主备延迟变大。
为了优化full_page_write,社区提供了一个patch,它的主要设计是创建两个共享内存块队列,checkpoint专用buffer队列和非checkpoint专用buffer队列,同时关闭full_page_write。当用户DML产生的数据buffer需要刷盘时,并不是立即刷到磁盘,而是先进入double write的buffer队列,当buffer队列满时,则将buffer队列里面的数据首先刷到特别的double write文件,然后再将数据刷到数据库文件。通过这种设计就不需要在checkpoint 之后在对数据页面的第一次写的时候会将整个数据页面写到 xlog 里面。当数据库需要恢复的时候,遍历所有double write文件里面的记录块,找到每个记录块对应的数据库page,然后对这个page进行checksum,如果page损坏,那么直接把记录块里面的内容覆盖到buffer数据。最后把double write文件删除,重新初始化buffer队列。

fullpagewrite_2_

总结

把full_page_write这个选项关闭会提高数据库执行速度以及减少xlog数量,但是可能导致系统崩溃或者掉电之后的数据库损坏。如果有减小部分页面写入风险的硬件支持(比如电池供电的磁盘控制器),或者文件系统支持(能够保证page写入原子性),可以把风险降低到一个可以接受的范围,那么可以考虑关闭这个选项,其他情况下建议打开这个选择。

目录
相关文章
|
前端开发 Java 关系型数据库
Caché学习笔记(一、cache安装与配置)
Caché学习笔记(一、cache安装与配置)
1829 0
Caché学习笔记(一、cache安装与配置)
|
9月前
|
人工智能 算法 搜索推荐
Geo专家于磊谈:Geo优化中被忽视的六大核心原则
在AI搜索时代,内容优化正从“流量为王”转向“信任为王”。Geo专家于磊提出“两大核心+四轮驱动”方法论,强调人性化Geo与内容交叉验证,倡导真实、专业、可信的内容建设,助力企业通过AI信任审计,实现高效获客与长期价值。
304 4
Geo专家于磊谈:Geo优化中被忽视的六大核心原则
|
JSON 关系型数据库 PostgreSQL
PostgreSQL 9种索引的原理和应用场景
PostgreSQL 支持九种主要索引类型,包括 B-Tree、Hash、GiST、SP-GiST、GIN、BRIN、Bitmap、Partial 和 Unique 索引。每种索引适用于不同场景,如 B-Tree 适合范围查询和排序,Hash 仅用于等值查询,GiST 支持全文搜索和几何数据查询,GIN 适用于多值列和 JSON 数据,BRIN 适合非常大的表,Bitmap 适用于低基数列,Partial 只对部分数据创建索引,Unique 确保列值唯一。
1668 15
|
druid Java 关系型数据库
Druid连接池的基本配置与使用
Druid连接池的基本配置与使用
4467 0
Druid连接池的基本配置与使用
|
SQL 关系型数据库 数据库
【一文搞懂PGSQL】4.逻辑备份和物理备份 pg_dump/ pg_basebackup
本文介绍了PostgreSQL数据库的备份与恢复方法,包括数据和归档日志的备份,以及使用`pg_dump`和`pg_basebackup`工具进行逻辑备份和物理备份的具体操作。通过示例展示了单库和单表的备份与恢复过程,并提供了错误处理方案。此外,还详细描述了如何利用物理备份工具进行数据损坏修复及特定时间点恢复(PITR)的操作步骤,以应对误操作导致的数据丢失问题。
|
存储 Kubernetes NoSQL
k8s学习--资源控制器StatefulSet详细解释与应用
StatefulSet 是 Kubernetes 中用于管理有状态应用的控制器,提供稳定的网络标识符和持久化存储能力。相较于 Deployment、DaemonSet 等无状态服务控制器,StatefulSet 支持有状态服务如 MySQL、Redis 等集群的部署和管理。本文详细介绍 StatefulSet 的概念、特点及部署方法,并通过具体示例展示了如何配置 NFS 存储及 StatefulSet 应用,确保每个 Pod 拥有独立且持久的数据存储空间。
915 0
|
关系型数据库 Shell 数据库
[postgres]启用归档模式
[postgres]启用归档模式
817 0
|
关系型数据库 PostgreSQL
|
C++
npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
npm : 无法将“npm”项识别为 cmdlet、函数、脚本文件或可运行程序的名称
8257 0
|
算法 C++ 容器
C++ std::remove/std::remove_if/erase用法探讨
std::remove 不会改变输入vector/string的长度。其过程相当于去除指定的字符,剩余字符往前靠。后面的和原始字符保持一致。
754 0