首页 > 其他分享 >full-page-writes

full-page-writes

时间:2023-08-28 22:03:49浏览次数:30  
标签:full buffer writes 数据库 全页 WAL 数据 page

全页写的概述

在数据库发生一次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)。

全页写的模式

非强制模式

对于修改操作,当启用全页写时,pg会在每个检查点之后、每个页面第一次发生变更时,将头数据和整个页面作为一条WAL记录写入WAL缓冲区。

  • 最近一次检查点之后,第一次修改的数据块会进行全页写,后续再修改时不会进行全页写,直到下一次检查点发生。

强制模式

对于备份操作,强制启用全页写,只要块发生变化,就会被整块写入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暴增

建议数据库备份时间点选在业务空闲时间段进行。

Oracle full-page-writes

1、不提供full-page-writes开关控制。

2、以下备份发生时自动启动全页写。

alter tablespace xxx begin backup;	
		alter database begin backup;

块不一致的场景

对PostgreSQL来说,块不一致可以发生在两种场景:

  • PG异常宕机(或者出现磁盘错误)时,数据文件中的页只写入了一部分。
  • 使用操作系统命令备份正在运行的数据库,备份途中源数据库可能被修改,此时得到的备份数据状态就是不一致的

无论是崩溃恢复还是备份还原的恢复,都无法基于不一致的数据块进行。

块不一致的原因

  • 操作系统进行I/O操作时,总是以块为单位,比如512字节、1KB等等。
  • 数据库块一般是操作系统块的整数倍,比如2k、4k、8k等等。
  • 块是数据库最小的I/O单位,当数据库写一个数据块时,操作系统需要I/O多次,可能在I/O过程中系统断电、磁盘故障等等原因导致一个数据块没有完整的写入,导致块不一致。

块不一致恢复

崩溃恢复

  • 通过checksum发现“部分写”的数据页,并将wal中保存的这个完整数据页覆盖当前损坏的数据页,然后再继续redo恢复整个数据库。

备份恢复

  • restore阶段,会直接还原不一致的块;但在recover阶段,会直接用WAL中一致的块对其进行覆盖,然后开始应用日志。

heap_xlog_insert

可以参考xlog的恢复代码

static void
heap_xlog_insert(XLogReaderState *record)
{
	XLogRecPtr	lsn = record->EndRecPtr;
	xl_heap_insert *xlrec = (xl_heap_insert *) XLogRecGetData(record);
	Buffer		buffer;
	Page		page;
	union
	{
		HeapTupleHeaderData hdr;
		char		data[MaxHeapTupleSize];
	}			tbuf;
	HeapTupleHeader htup;
	xl_heap_header xlhdr;
	uint32		newlen;
	Size		freespace = 0;
	RelFileNode target_node;
	BlockNumber blkno;
	ItemPointerData target_tid;
	XLogRedoAction action;

	XLogRecGetBlockTag(record, 0, &target_node, NULL, &blkno);
	ItemPointerSetBlockNumber(&target_tid, blkno);
	ItemPointerSetOffsetNumber(&target_tid, xlrec->offnum);

	/*
	 * The visibility map may need to be fixed even if the heap page is
	 * already up-to-date.
	 */
	if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
	{
		Relation	reln = CreateFakeRelcacheEntry(target_node);
		Buffer		vmbuffer = InvalidBuffer;

		visibilitymap_pin(reln, blkno, &vmbuffer);
		visibilitymap_clear(reln, blkno, vmbuffer, VISIBILITYMAP_VALID_BITS);
		ReleaseBuffer(vmbuffer);
		FreeFakeRelcacheEntry(reln);
	}

	/*
	 * If we inserted the first and only tuple on the page, re-initialize the
	 * page from scratch.
	 */
	if (XLogRecGetInfo(record) & XLOG_HEAP_INIT_PAGE)
	{
		buffer = XLogInitBufferForRedo(record, 0);
		page = BufferGetPage(buffer);
		PageInit(page, BufferGetPageSize(buffer), 0);
		action = BLK_NEEDS_REDO;
	}
	else
		action = XLogReadBufferForRedo(record, 0, &buffer);
	if (action == BLK_NEEDS_REDO)
	{
		Size		datalen;
		char	   *data;

		page = BufferGetPage(buffer);

		if (PageGetMaxOffsetNumber(page) + 1 < xlrec->offnum)
			elog(PANIC, "invalid max offset number");

		data = XLogRecGetBlockData(record, 0, &datalen);

		newlen = datalen - SizeOfHeapHeader;
		Assert(datalen > SizeOfHeapHeader && newlen <= MaxHeapTupleSize);
		memcpy((char *) &xlhdr, data, SizeOfHeapHeader);
		data += SizeOfHeapHeader;

		htup = &tbuf.hdr;
		MemSet((char *) htup, 0, SizeofHeapTupleHeader);
		/* PG73FORMAT: get bitmap [+ padding] [+ oid] + data */
		memcpy((char *) htup + SizeofHeapTupleHeader,
			   data,
			   newlen);
		newlen += SizeofHeapTupleHeader;
		htup->t_infomask2 = xlhdr.t_infomask2;
		htup->t_infomask = xlhdr.t_infomask;
		htup->t_hoff = xlhdr.t_hoff;
		HeapTupleHeaderSetXmin(htup, XLogRecGetXid(record));
		HeapTupleHeaderSetCmin(htup, FirstCommandId);
		htup->t_ctid = target_tid;

		if (PageAddItem(page, (Item) htup, newlen, xlrec->offnum,
						true, true) == InvalidOffsetNumber)
			elog(PANIC, "failed to add tuple");

		freespace = PageGetHeapFreeSpace(page); /* needed to update FSM below */

		PageSetLSN(page, lsn);

		if (xlrec->flags & XLH_INSERT_ALL_VISIBLE_CLEARED)
			PageClearAllVisible(page);

		/* XLH_INSERT_ALL_FROZEN_SET implies that all tuples are visible */
		if (xlrec->flags & XLH_INSERT_ALL_FROZEN_SET)
			PageSetAllVisible(page);

		MarkBufferDirty(buffer);
	}
	if (BufferIsValid(buffer))
		UnlockReleaseBuffer(buffer);

	/*
	 * If the page is running low on free space, update the FSM as well.
	 * Arbitrarily, our definition of "low" is less than 20%. We can't do much
	 * better than that without knowing the fill-factor for the table.
	 *
	 * XXX: Don't do this if the page was restored from full page image. We
	 * don't bother to update the FSM in that case, it doesn't need to be
	 * totally accurate anyway.
	 */
	if (action == BLK_NEEDS_REDO && freespace < BLCKSZ / 5)
		XLogRecordPageWithFreeSpace(target_node, blkno, freespace);
}

/*
 * Handles MULTI_INSERT record type.
 */
static void

参考

PostgreSQL技术内幕:事务处理深度探索

阿里云直播—pg-full-page机制与原理

PG技术大讲堂直播—PostgreSQL Full-Page Writes 全页写

标签:full,buffer,writes,数据库,全页,WAL,数据,page
From: https://blog.51cto.com/u_13482808/7267286

相关文章

  • 利用GitHub 的Actions自动同步gitee仓库,并Gitee Pages 自动部署项目
    Gitee同步GitHub仓库GitHub有时候访问速度慢,加载不了图片等问题。过程记录GitHub的Actions会处理.github下的工作流文件夹workflows。只要在g项目根目录下创建.github/workflows/,在这个文件夹里再创建Sync.yml文件,填入以下代码。当产生push操作就会自动同步gitee<divid="......
  • 公司来了个大佬,把 FullGC 40 次/天优化为 10 天 1 次,太秀了~!
    来源:https://heapdump.cn/article/1859160通过这一个多月的努力,将FullGC从40次/天优化到近10天才触发一次,而且YoungGC的时间也减少了一半以上,这么大的优化,有必要记录一下中间的调优过程。对于JVM垃圾回收,之前一直都是处于理论阶段,就知道新生代,老年代的晋升关系,这些知......
  • Windows Server2008R2 服务器Paged Pool占用过高的问题
    这台服务器一直运行的好好的,但最近发现经常内存占用了99%,重启后过几天内存又涨到99%。运行的应用软件占的内存并不高,任务管理器所有进程占用内存加起来也远远不到99%。下载了RamMap,发现是PagedPool占用了绝大多数的内存; 下载poolmon.exe,终端中运行poolmon.exe-p-b,再按下......
  • platform_device_register和platform_device_register_full的区别
    platform_device_register和platform_device_register_full都是用于在Linux内核中注册平台设备的函数,但是它们之间存在一些区别。platform_device_registerplatform_device_register函数用于注册一个平台设备。它接受一个指向platform_device结构体的指针作为参数,该结构体表示......
  • 当小白遇到FullGC
    起初没有人在意这场GC,直到它影响到了每一天!前言本文记录了一次排查FullGC导致的TP99过高过程,介绍了一些排查时思路,线索以及工具的使用,希望能够帮助一些新手在排查问题没有很好的思路时,提供一些思路,让小白也能轻松解决FullGC问题,文中实际提到的参数配置不一定适合其他......
  • MIT6.s081/6.828 lectrue07:Page faults 以及 Lab5 心得
    本篇博客主要是复习MIT6.s081/6.828lectrue07:Pagefaults以及记录Lab5:COWfork的心得值得一提的是,2020年之前的版本第5个lab是lazyalloction,但是到了2020年之后就换成了难度稍高一点的COWfork,有兴趣的小伙伴可以把lazyalloction也一起做一做~毕竟这些lab......
  • 当小白遇到FullGC | 京东云技术团队
    起初没有人在意这场GC,直到它影响到了每一天!前言本文记录了一次排查FullGC导致的TP99过高过程,介绍了一些排查时思路,线索以及工具的使用,希望能够帮助一些新手在排查问题没有很好的思路时,提供一些思路,让小白也能轻松解决FullGC问题,文中实际提到的参数配置不一定适合其他业务场......
  • UE5 PostProcess 三种常用的fullscreen mask
    前言本篇总结了三个常用的mask,若需要更多的mask形状可以去学学SDF函数,这个笔者还在学,有点复杂,等后面有时间在总结一下推导过程叭为什么需要全屏mask在扭曲效果一文中,我们实现了全屏的扭曲效果,但有时我们需要的只是局部扭曲,比如场景四边的边缘并不产生扭曲效果,而在中间产生,这时......
  • SAP Fiori Tools Application Modeler Page Map 标题的数据源
    按照笔者下面这篇教程的文章,安装了SAPFioriTools之后:SAPFioriElements开发教程-从入门到精通1.SAPFioriElements开发环境的搭建和开发准备工作我们就可以在VisualStudioCode的命令行里,使用PageMap,以一个图形化的界面,显示FioriElements应用的结构了:图......
  • SAP Fiori Tools Page Map 的实现详解和故障排除试读版
    本教程前面的文章,笔者介绍了使用SAPFioriTools这个工具里包含的ApplicationModeler提供的PageMap功能,来给FioriElementsListReport应用添加自定义列的详细步骤。10.如何通过扩展(Extension)的方式给SAPFioriElementsListReport的表格新增列我们在Visua......