PostgreSQL buffer标签的介绍

在PostgreSQL中,每个buffer都有一个对应的buf_desc,buffer descriptor(描述符)。buf_desc用于描述buffer的有关信息,例如buffer上存的是哪个页面、这个页面是否是脏页面。

首先说一下数据结构中相关字段。

typedef struct BufferDesc
{
	BufferTag	tag;			/* ID of page contained in buffer */
	int			buf_id;			/* buffer's index number (from 0) */

	/* state of the tag, containing flags, refcount and usagecount */
	pg_atomic_uint32 state;

	int			wait_backend_pgprocno;	/* backend of pin-count waiter */
	int			freeNext;		/* link in freelist chain */
	LWLock		content_lock;	/* to lock access to buffer contents */
} BufferDesc;

tag:表示buffer上存的是哪个页面

buf_id:表示buffer在数组中下标。PG中申请了大数组用于缓存buffer,一般称之为bufferpool

state: 32bit位字段,某些bit用于表示特定的标签。

content_lock:buffer的内容锁,用于控制页面的访问。具体可以看我之前写的文章

本次借助buf_desc的标签,来了解buffer相关的机制。具体的标签如下

#define BM_LOCKED				(1U << 22)	/* buffer header is locked */
#define BM_DIRTY				(1U << 23)	/* data needs writing */
#define BM_VALID				(1U << 24)	/* data is valid */
#define BM_TAG_VALID			(1U << 25)	/* tag is assigned */
#define BM_IO_IN_PROGRESS		(1U << 26)	/* read or write in progress */
#define BM_IO_ERROR				(1U << 27)	/* previous I/O failed */
#define BM_JUST_DIRTIED			(1U << 28)	/* dirtied since write started */
#define BM_PIN_COUNT_WAITER		(1U << 29)	/* have waiter for sole pin */
#define BM_CHECKPOINT_NEEDED	(1U << 30)	/* must write for checkpoint */
#define BM_PERMANENT			(1U << 31)	/* permanent buffer (not unlogged,
											 * or init fork) */

BM_LOCKED:表示state被锁住

BM_LOCKED 就是state上的锁,通过cas(compare-and-swap)之类的机制,保证只有一个线程会设置上这字段,当该线程设置BM_LOCKED,其他线程将无法设置 BM_LOCKED 。从而保证只有该线程此刻可以改state上其他bit位。

相关接口

LockBufHdr:在state加上BM_LOCKED这个标签
UnlockBufHdr:在state上消除BM_LOCKED这个标签

BM_DIRTY:表示脏页

BM_JUST_DIRTIED:表示页面在刷盘的时候,又变成脏页

BM_DIRTY代表脏页,就是页面被改动,相较于磁盘对应的页面,内存中页面就是脏页。

DBMS将脏页刷到磁盘这个流程需要一段时间。当一个页面确定要刷盘,再到真正完成刷盘。在这过程中,页面可能会再次设置为脏页(其他业务改了这个页面)。那么该怎么判断出这个情况了?很简单,再增加一个标签,也就是BM_JUST_DIRTIED

1)设置脏页的时候。设置BM_DIRTY和BM_JUST_DIRTIED
2)确定要刷盘,取消BM_JUST_DIRTIED
3)刷盘完成,准备取消 BM_DIRTY;如果此时发现页面有BM_JUST_DIRTIED,那就说明页面又被设置脏页了,BM_DIRTY不能取消,这个页面还要刷盘。没有BM_JUST_DIRTIED,则可以取消BM_DIRTY

相关接口

FlushBuffer: 刷盘
MarkBufferDirty: 设置为脏页;调用后,BM_DIRTY标签会被设置上

为什么会允许在刷盘的过程中,页面可以再次设置为脏页了,估计也是为了提高业务并发,提升性能。

补充一下,刷盘的时候,我们占用的是IO锁;设置为脏页的时候,用的是content锁(内容锁)。

BM_VALID:代表是个有效页面,可以被合法使用

BM_TAG_VALID:代表页面tag,已经在表中存储

BM_IO_IN_PROGRESS:代表页面正在IO中

BM_IO_ERROR:代表IO失败,是个无效页面

页面的bufferpool是存放页面,那么肯定需要一个"表",这个记录这个buffer,记录哪个页面。以及我们也需要通过页面唯一标识符,查看这个页面是不是在bufferpool中。这个表在postgreSQL就是一个hashmap。key就是页面的表示符(tag),value就是具体哪个buffer(buffer大数组的下标)。

1)当业务进程需要使用某个页面的时候,根据页面tag,查看是否在hashmap中,如果不在的话,那么在hashmap中创建一个键值对,此时就会打上BM_TAG_VALID标签。

2)当前页面不在bufferpool中,我们就需要将页面从磁盘加载到内存中。在加载的过程中,需要打上BM_IO_IN_PROGRESS标签,表示页面正在IO中。

3)我们只允许一个进程进行该页面的IO操作。当页面完成IO,成功加载到磁盘上,此时我们将BM_IO_IN_PROGRESS去除,然后打上BM_VALID标签,代表页面是一个合法的页面

4)BM_IO_ERROR,从字面意思上看,是页面没有加载成功,设置为无效页面。在PG14中相关流程还没有看懂,后面再补充

相关接口

ReadBuffer_common: 读页面
BufferAlloc: 确定页面是否在bufferpool,没有在hashmap创建一个键值对;有的话就放回对应buffer
TerminateBufferIO:结束IO

BM_PIN_COUNT_WAITER:代表有人等pin降至为0

pin相关的机制,已在之前的文章介绍。《链接》。

本次简要介绍pin机制,当要使用一个页面,页面的引用计数+1,也就是页面被pin住。其他线程使用这个页面,引用计数继续+1。当本次流程不再使用页面时,才会将引用计数-1。页面在引用计数等于0的情况下,才允许淘汰。如果页面引用计数大于0,说明有线程正在使用这个页面,所以不能淘汰。

在多数流程中,页面被一个线程pin住后,还能被另一个线程pin住。但有一些流程要求页面除了自己,无其他线程pin住。例如页面clean流程、vacuum相关流程。

当线程pin住页面,如果有其他线程也pin住该页面,那么只要等其他线程解除pin;此时我们需要打上BM_PIN_COUNT_WAITER,代表这个页面,有线程正在等待pin的值变成1。其他线程在解除pin,通过这个标签,以及pin值等于1,从而通知等待的线程。

BM_PERMANENT:代表常住页面

多数表、索引等对应的页面都是常驻页面,但也有一些例外情况。 unlogging 表就是例外,这种表相关页面就没有这个标签。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值