postgresql Buffer并发控制一

 


目录

前言

概述

同步控制方法

详细分解

结尾


前言

本文是基于postgresql 15的代码进行分析解读,演示是在centos8系统上进行。


概述

在postgresql 为了加速数据的读写性能,在共享内存中增加了数据缓冲区,也就是我们常说的buffer。buffer是在多进程之间共享,所以需要增加冲突处理,也就是并发控制,本文主要介绍并发控制的方法,以及该种方法的用途。

同步控制方法

        1、查找缓冲区时的并发控制

从buffer的结构来讲,为了提升性能,设计了三层结构:buffermapping hash table, buffer describe array, buffer array,所以每层都会涉及到并发同步控制。

  1. buffer查找,也要从buffermapping的hash结构中查找tag对应的buffer id;此时需要用到hash分段锁 BufMappingPartitionLock。
  2. 找到buffer id后,先从buffer 描述符中查找信息,为了读写互斥,就需要加锁 LockBufHdr;

至此就可以查找到有效的buffer了。

                2、从磁盘加载/flush时的并发控制

当查找到buffer后,有两种情况:

一是正常使用时,buffer state还不是valid时,说明磁盘中没有块,需要从磁盘加载;

二是当查找到块后需要flush时,就要检查块是否为脏BM_DIRTY,如果非脏就不需要flush,如果为脏时;

以上两种情况下,会增加 BM_IO_IN_PROGRESS IO处理标志,表明本进程正在处理IO,其它backend或辅助进程就需要等待;当然设置标记时,需要加描述符锁;

        3、引用使用时的并发控制

查找到buffer后,需要标记当前buffer被引用,避够被替换出去的可能。

此处调用PinBuffer 来设置buffer state上的 usage count和ref count,同时在本地也记录ref count,在设置时必须等到锁释放;当然此时buffer是否valid并不确定。

此处buffer 的本地ref count是为了提升性能,减少冲突竞争,当本地进程再次引用时,就只增加本地的ref count;

        (1)在buffer中查找内容时的并发控制

        (2) 修改buffer时的并发控制

详细分解

1、BufMappingPatitionLock

BufferMapping 分段hash加分段锁,避够与修改冲突,同时此处进行了分段加锁,对hash分了NUM_BUFFER_PARTITIONS段,避免了对整个hash的加锁,减少冲突的范围,提升并发访问性能,当然有更大并发需求时,也可以调大这个值。

static inline LWLock *

BufMappingPartitionLock(uint32 hashcode)

{

return &MainLWLockArray[BUFFER_MAPPING_LWLOCK_OFFSET +

BufTableHashPartition(hashcode)].lock;

}

LWLockAcquire(newPartitionLock, LW_SHARED);

2、BufferHeaderLock

对于buffer描述符加锁操作接口:

buf_state = LockBufHdr(bufHdr);

3、BufferHeader state

Buffer state在 Buffer 描述符中,是一个32bit的字段,按位被分成了三段。

低18bits,0-17位是refcount值

18-21位,是usage count值

22-31位,也就是高10位,是flag值,

其中重要的两个flag:加锁BM_LOCKED标记是22位,BM_VALID是24位; 当flag为BM_VALID时,才能被使用,也就是数据是有效的。

4、引用计数和使用计数

当共享内存中已经有查找的tag对应的buffer,此时在buffer 描述符上更新ref count和usage count,表明有backend在引用,不能被替换出去,也不能进行页面prune。本backend第一次查找该buffer时,还会加到本地资源管理,并对本地refcount递增1。如果找到的buffer,已经在上一次ReadBuffer时增加到本地资源管理了,此时只增加本地引用计数就可以,避够并发竞争。

获取到buffer后,refcount,与usagecount,在buffer描述符上state进行递增,如果是有替换策略,非默认usagecount只加到1。

5、IO控制标记 IO_IN_PROGRESS

当找到的buffer的state不是BM_VALID,说明buffer没有被加载或正在被别的backend加载,此时通过IO_IN_PROGRESS标记来判断,如果有其它backend正在加载,当前backend就需要等待,等IO_IN_PROGRESS标记撤销,如果自己需要加载,就增加标记,然后进行磁盘加载,结束后,取消标记。

6、buffer内容锁

对于buffer内容的lock操作接口:

LWLockAcquire(BufferDescriptorGetContentLock(bufHdr),

  LW_EXCLUSIVE);

对于buffer的lock操作其它接口:

extern void UnlockBuffers(void);

extern void LockBuffer(Buffer buffer, int mode);

extern bool ConditionalLockBuffer(Buffer buffer);

extern void LockBufferForCleanup(Buffer buffer);

extern bool ConditionalLockBufferForCleanup(Buffer buffer);

LockBuffer

普通buffer加锁接口

LockBufferForCleanup

在删除内容时调用,与LockBuffer的区别是,会等待pin为1,也就是其它backend没有使用时,再加锁;主要在vacuum,恢复,备机回放场景下调用。

7、释放buffer引用

extern void ReleaseBuffer(Buffer buffer);

extern void UnlockReleaseBuffer(Buffer buffer);


结尾

作者邮箱:[email protected]
如有错误或者疏漏欢迎指出,互相学习。

注:未经同意,不得转载!

猜你喜欢

转载自blog.csdn.net/senllang/article/details/129950572