PostgreSQL源码分析——bgwriter

为什么会有bgwriter

bgwriter进程主要负责将共享缓冲区(Buffer)中的脏页刷盘,这个进程主要是从数据库性能的考虑而加的,如果没有这个进程,数据库一样可以工作。所以,这里重点理解的就是bgwriter进程对性能的影响。

我们前面讲过,一条插入语句的执行过程,先在Buffer中找到空闲页,在页中插入元组,暂不刷盘,而且先构造WAL日志,将WAL日志刷盘,再由后台进程(bgwriter)刷盘。之所以这么设计就是出于性能的考虑,每次写后,频繁的进行刷盘会降低性能。比如,连续进行100次插入,每次插入的都是同一个页,就会造成对这个页频繁的进行刷盘,而通过bgwriter以及wal,则转换为写WAL,再对该脏页刷1次盘即可,设计WAL,bgwriter其中目的之一都是为了降低刷盘的频率。

其二,在有WAL后,那我一直不对脏页进行刷盘行不行?答案是肯定不行,即使bgwriter不进行刷盘,缓冲区也会进行页淘汰,缓冲区大小是有限的,当缓冲区满了时,又需要从磁盘中读数据页到缓冲区中,就必须将缓冲区中的部分页进行淘汰,目前的算法是时钟扫描算法,如果选择淘汰的页是脏页,则需要将脏页进行刷盘,这会导致查询或者更新需要更长的时间,自然性能降低了。周期性的进行脏页刷盘,避免了在查询过程中因为缓冲区淘汰页导致的刷盘,避免了因此导致的性能降低。

其三,bgwriter进行周期性的刷盘,对性能的平稳有益,能够一定程度的避免性能的抖动,使得IO操作尽可能的被平滑的处理了。不单单是bgwriter,其他进程也有这方面设计的思考,比如autovacuum进程。

参数说明

bgwriter涉及到以下参数设置:

#bgwriter_delay = 200ms                 # 10-10000ms between rounds
#bgwriter_lru_maxpages = 100            # max buffers written/round, 0 disables
#bgwriter_lru_multiplier = 2.0          # 0-10.0 multiplier on buffers scanned/round

表示系统每间隔bgwriter_delay指定的时间启动进程bgwriter,扫描缓冲区,写出至多bgwriter_lru_multiplier * N个脏页,并且不超过bgwriter_lru_maxpages值的限制。其中N是最近一段时间在两次bgwriter运行期间系统新申请的缓冲页数。

源码分析

bgwriter进程,核心流程很清晰,就是间隔一段时间进行Buffer刷盘,主流程如下:

/** Main entry point for bgwriter process** This is invoked from AuxiliaryProcessMain, which has already created the* basic execution environment, but not enabled signals yet.*/
void BackgroundWriterMain(void)
{// 注册信号处理函数pqsignal(SIGTERM, SignalHandlerForShutdownRequest);// ...// 错误处理/* Loop forever */for (;;){bool		can_hibernate;int			rc;/* Clear any already-pending wakeups */ResetLatch(MyLatch);HandleMainLoopInterrupts();/* Do one cycle of dirty-buffer writing. */can_hibernate = BgBufferSync(&wb_context);// ...// 等待,直到收到信号或BgWriterDelay超时/* Sleep until we are signaled or BgWriterDelay has elapsed. */rc = WaitLatch(MyLatch,WL_LATCH_SET | WL_TIMEOUT | WL_EXIT_ON_PM_DEATH,BgWriterDelay /* ms */ , WAIT_EVENT_BGWRITER_MAIN);// ...}
}

其主要实现是在BgBufferSync函数中实现的,让我们看一下这个函数。先思考一下函数设计的关键点,第1个问题,Buffer中页面那么多,从哪里开始? 第2个问题,每次刷盘刷多少个页面?关于从哪里开始,这个问题,首先看bgwriter存在的意义,其中要避免缓冲区淘汰脏页面进行刷盘,所以,bgwriter扫描页面,最好是领先于时钟扫描算法一轮,这样才能起到效果。而上面讲到的2个参数就是解决每次刷多少个页面相关的参数。下面的函数中,会涉及到上面2个问题,计算每次刷多少个是最优的,在哪里开始扫描。具体的算法,我们这里不进行分析,可参考《PostgreSQL技术内幕:事务处理探索》第4.12.2章节。

/* BgBufferSync -- Write out some dirty buffers in the pool. */
bool BgBufferSync(WritebackContext *wb_context)
{/* info obtained from freelist.c */int			strategy_buf_id;uint32		strategy_passes;uint32		recent_alloc;// .../** Find out where the freelist clock sweep currently is, and how many* buffer allocations have happened since our last call.*/strategy_buf_id = StrategySyncStart(&strategy_passes, &recent_alloc);// 执行扫描,刷盘/* Execute the LRU scan */while (num_to_scan > 0 && reusable_buffers < upcoming_alloc_est){int			sync_state = SyncOneBuffer(next_to_clean, true,wb_context);if (++next_to_clean >= NBuffers){next_to_clean = 0;next_passes++;}num_to_scan--;if (sync_state & BUF_WRITTEN){reusable_buffers++;if (++num_written >= bgwriter_lru_maxpages){BgWriterStats.m_maxwritten_clean++;break;}}else if (sync_state & BUF_REUSABLE)reusable_buffers++;}// ...
}

最后是将Buffer刷盘的实现,我们看一下SyncOneBuffer,作用是尝试将单个缓冲区页面刷入磁盘文件。

/* SyncOneBuffer -- process a single buffer during syncing. */
static int SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context)
{BufferDesc *bufHdr = GetBufferDescriptor(buf_id);int			result = 0;uint32		buf_state;BufferTag	tag;ReservePrivateRefCountEntry();/** Check whether buffer needs writing.** We can make this check without taking the buffer content lock so long* as we mark pages dirty in access methods *before* logging changes with* XLogInsert(): if someone marks the buffer dirty just after our check we* don't worry because our checkpoint.redo points before log record for* upcoming changes and so we are not required to write such dirty buffer.*/buf_state = LockBufHdr(bufHdr);if (BUF_STATE_GET_REFCOUNT(buf_state) == 0 &&BUF_STATE_GET_USAGECOUNT(buf_state) == 0){result |= BUF_REUSABLE;}else if (skip_recently_used){/* Caller told us not to write recently-used buffers */UnlockBufHdr(bufHdr, buf_state);return result;}if (!(buf_state & BM_VALID) || !(buf_state & BM_DIRTY)){/* It's clean, so nothing to do */UnlockBufHdr(bufHdr, buf_state);return result;}/** Pin it, share-lock it, write it.  (FlushBuffer will do nothing if the* buffer is clean by the time we've locked it.)*/PinBuffer_Locked(bufHdr);LWLockAcquire(BufferDescriptorGetContentLock(bufHdr), LW_SHARED);FlushBuffer(bufHdr, NULL);LWLockRelease(BufferDescriptorGetContentLock(bufHdr));tag = bufHdr->tag;UnpinBuffer(bufHdr, true);ScheduleBufferTagForWriteback(wb_context, &tag);return result | BUF_WRITTEN;
}

下面就是将Buffer写入磁盘的具体过程,详细可参考函数FlushBuffer

  1. 依据Buffer中的描述信息,打开指定的表文件
  2. 校验并copy Buffer中待写入的数据
  3. 将Buffer的数据写入打开的表文件中
static void
FlushBuffer(BufferDesc *buf, SMgrRelation reln)
{/* Find smgr relation for buffer */if (reln == NULL)reln = smgropen(buf->tag.rnode, InvalidBackendId);bufBlock = BufHdrGetBlock(buf);/** Update page checksum if desired.  Since we have only shared lock on the* buffer, other processes might be updating hint bits in it, so we must* copy the page to private storage if we do checksumming.*/bufToWrite = PageSetChecksumCopy((Page) bufBlock, buf->tag.blockNum);/* bufToWrite is either the shared buffer or a copy, as appropriate. */smgrwrite(reln,buf->tag.forkNum,buf->tag.blockNum,bufToWrite,false);}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/29596.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SQL注入-中篇

SQL盲注 一、时间盲注 模拟环境&#xff1a;Less-9 概述 延迟注入&#xff0c;一种盲注的手法&#xff0c;提交对执行时间敏感的sql语句&#xff0c;通过执行时间的长短来判断是否执行成功。 时间注入函数 sleep() if() ascii() substring() length() mid()判断是否存在延…

存储文件夹下所有.cpp和.h的代码到对应的txt文件里

最近大半年刷了160多天的题&#xff0c;每次刷的时候都要新建一个VS文件&#xff0c;所以文件内存太大了&#xff0c;又舍不得删&#xff0c;就用ai整了一个脚本&#xff0c;可将当前路径下的所有文件里的.cpp和.h文件储存到相应名字的txt文件里&#xff0c;若文件夹下还有文件…

SpringBoot + Maven 项目的创建

文章目录 1、Maven2、SpringBoot3、二者之间的联系4、项目的创建 在创建项目之前&#xff0c;肯定要知道他们之间的区别 1、Maven maven是一个跨平台的项目管理工具。它是Apache的一个开源项目&#xff0c;主要服务于基于Java平台的项目构建、依赖管理和项目信息管理。 比如说…

Spring注解----------@Deprecated

情景&#xff1a; 在我们开发过程中&#xff0c;有时候会遇到我们需要将几个类中的方法集中到一个类中&#xff0c;但是我们又不希望把我们的原来的类删掉&#xff08;就是单纯的不想删除&#xff0c;都是我写的代码我不想杀死我的结晶&#xff0c;不能说我写的是shi&#xff…

为什么要选择华为 HCIE-Security 课程?

2020 年我国网络安全市场规模达到 680 亿元&#xff0c;同比增长 25%。随着对网络安全的愈加重视及布局&#xff0c;市场规模将持续扩大。 近年来&#xff0c;随着“云大物工移智”等新兴技术的快速发展和普及应用&#xff0c;数字化已经融入社会经济生活的方方面面&#xff0c…

自我激励学习提升语言模型的推理能力

随着人工智能技术的快速发展&#xff0c;语言模型&#xff08;LMs&#xff09;在各种下游任务中展现出了卓越的能力。特别是在少样本&#xff08;few-shot&#xff09;和零样本&#xff08;zero-shot&#xff09;学习环境中&#xff0c;通过吸收特定任务的指令和示例&#xff0…

Makefile 编译文件中的c文件,输出可执行文件main

Makefile文件 CC aarch64-linux-gnu-gcc CFLAGS SRCS $(wildcard *.c) OBJS $(SRCS:.c.o) TARGET mainall: $(TARGET)$(TARGET): $(OBJS)$(CC) $(CFLAGS) -o $ $^%.o: %.c$(CC) $(CFLAGS) -c $< -o $clean:rm -f $(OBJS) $(TARGET)执行make之后输出&#xff1a; to…

CLIP: Learning Transferable Visual Models From Natural Language Supervision

1、引言 论文链接&#xff1a;ReadPaper 现在最先进的计算机视觉系统都是训练模型来预测一组固定的、预定义好的目标类别&#xff08;如 ImageNet 的 1000 类和 COCO 的 80 类&#xff09;。这种受限制的监督形式限制了它们的通用性和可用性&#xff0c;因为需要额外的标记数据…

深入探索 MongoDB GridFS:高效大文件存储与管理的全面指南

GridFS 是 MongoDB 的一个规范&#xff0c;用于存储和检索超过 BSON 文档大小限制&#xff08;16MB&#xff09;的文件。与传统的文件系统不同&#xff0c;GridFS 可以将一个大文件分割成多个小块&#xff0c;并存储在 MongoDB 的两个集合中&#xff1a;fs.files 和 fs.chunks。…

光纤通信基础(光纤的构造、工作原理、色散、工作频段、损耗、分类、不同标准及应用、接口类型、常见标示方法、熔接)

文章目录 光纤的构造&#xff1a;纤芯、包层、涂覆层光纤的工作原理&#xff1a;利用全反射来传输光信号光纤的色散光纤的工作频段光纤的损耗光纤的分类光纤的不同标准及应用光纤的接口类型&#xff08;SC、LC、ST、FC&#xff09;光纤的常见标示方法&#xff1a;如“FC/PC”&a…

项目准备和启动

1.什么是项目建议书&#xff1f; 2.项目建议书的内容 3.可行性分析方法 4.项目组织结构&#xff08;职能型 项目型 矩阵型&#xff09; 5.项目管理层决策层执行层之间的关系 6.软件项目的可行性分析包括哪几个方面&#xff1f;影响决策的关键因素又是什么&#xff1f; 软件项目…

mysql的双机热备

一般主从复制是主服务器数据库可读可写&#xff0c;从服务器数据库只读&#xff0c;而双机热备是主从服务器的数据库双向复制&#xff0c;从服务器数据库可读也可写。 说到主从复制不得不说–读写分离&#xff0c;此介绍请看这一篇 一文浅谈“读写分离”技术 双机热备的概念简…

鸿蒙开发实战:灵活定制Tabs组件,实现个性化页签布局

闪客 沉默的闪客 2024-06-16 20:01 陕西 大家好&#xff0c;又一个项目已经基本完成 是一个元服务英语单词卡片项目&#xff0c;后面一步一步的进行分析拆解&#xff0c;今天来实现一个Tabs组件自定义界面开发。 鸿蒙ArkUI 开发的时候&#xff0c;Tabs 组件很常用&#xff0c;…

CDA二级(Level II)数据分析师——考试内容梳理三(简单重点案例)

PR曲线是基于召回率的准确性来进行判断的&#xff1b; 混淆矩阵&#xff1a; ROC曲线以真阳性率&#xff08;敏感性、召回率&#xff09;为纵坐标&#xff0c;假阳性率&#xff08;1-特异性&#xff09;为横坐标 假阳性率&#xff1a;负样本中被误认为正样本的概率 FP/FPTN 真…

什么是作用域?作用域有哪几种?

**作用域&#xff08;Scope&#xff09;**是一个程序设计中的重要概念&#xff0c;它指的是变量、函数和对象在程序中可访问和引用的区域。作用域决定了代码块中声明的变量、函数和对象的可见性和生命周期。不同的编程语言提供了不同的作用域类型&#xff0c;以下是几种常见的作…

基于B/S版java语言+SpringBoot技术开发的云HIS系统源码 HIS系统住院业务模块常见问题及解决方案

基于B/S版java语言SpringBoot技术开发的云HIS系统源码 HIS系统住院业务模块常见问题及解决方案 随着医疗技术的不断提高&#xff0c;住院治疗已成为许多病人的常规选择。但是&#xff0c;住院治疗不仅需要医护人员的精心照顾&#xff0c;也需要个高效的信息系统来保证整个治疗过…

C语言程序设计-8 函 数

8.1 概述 在前面已经介绍过&#xff0c;&#xff23;源程序是由函数组成的。虽然在前面各章的程序中大都只有一个 主函数 main()&#xff0c;但实用程序往往由多个函数组成。函数是&#xff23;源程序的基本模块&#xff0c;通过对函 数模块的调用实现特定的功能。&#xff23…

HBase学习之HBaseAPI

HBase学习之HBaseAPI: package com.shujia.base;import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.*; import org.apache.hadoop.hbase.client.*; import org.apache.hadoop.hbase.util.Bytes; import org.junit.After; import org.junit.Before…

Unity制作透明材质直接方法——6.15山大软院项目实训

之前没有在unity里面接触过材质的问题&#xff0c;一般都是在maya或这是其他建模软件里面直接得到编辑好材质的模型&#xff0c;然后将他导入Unity里面&#xff0c;然后现在碰到了需要自己在Unity制作透明材质的情况&#xff0c;所以先搜索了一下有没有现成的方法&#xff0c;很…

删除名为 `XXXX` 的 conda 环境的命令

要删除名为 musetalk 的 conda 环境,您可以使用以下命令: conda remove --name musetalk --all这个命令会删除 musetalk 环境及其中安装的所有包。 执行上述命令后,您可以使用 conda env list 命令验证该环境是否已被成功删除。 如果您想要保留该环境中安装的某些包,可以先使…