02PG缓存-缓存锁

PostgreSQL中shared_buffers定义数据缓冲区的大小,会在操作系统内存开辟一块共享的区域,既然是共享的,所有的后台进程都可以访问,必然会导致争用,那么哪些进程可以访问并修改,哪些进程不可以,又怎样保证数据的一致性,这就需要锁来实现。

需要注意的是,这里的锁是内存锁,和表或索引的行锁、表锁是没有任何关系的。

1.BufMappingLock

BufMappingLock保护整个缓冲表的数据完整性,他是一种轻量锁,它有共享模式和独占模式,当在缓冲表中查询条目时,后端进程会持有共享的BufMappingLock,插入或删除条目时,后端进程会持有独占的BufMappingLock。

为了减少对BufMappingLock的争用,BufMappingLock会被分为多个分区,每个BufMappingLock分区保护着一部分的缓冲表。在9.4之前,BufMappingLock默认会分为16个分区,在9.4之后,BufMappingLock默认分为128个。

当然,缓存表不仅仅只有BufMappingLock,例如缓存表内部会使用自旋锁(spin lock) 来删除数据项。

2.缓存描述符相关的锁

在9.6之前,和缓存描述符相关的有三种锁:内容锁(content_lock)、IO进行锁(io_in_progress_lock)和自旋锁(spin lock)。其中内容锁和IO进行锁是两种轻量级锁,控制对缓冲池的访问。自旋锁控制对缓存描述符的访问。

内容锁 是一个典型的强制限制访问的锁,他有共享和独占两种模式。

当读取页面时,后端进程以共享模式获取页面相应缓冲区描述符中的content_lock。

行下列操作之一时,则会获取独占模式的 content_lock。

  • 将行(即元组)插入页面,或更改页面中元组的 txmin/t xmax 字段时(简单地说,这些字段会在相关元组被删除或更新行时发生更改)。
  • 物理移除元组,或压紧页面上的空闲空间。
  • 冻结页面中的元组。

IO进行锁(io_in_progress_lock)用于等待缓冲区上的I/0完成。当PostgreSQL进程加载/写入页面数据时,该进程在访问页面期间,持有对应描述符上独占的 io_in_progress_lock。

当访问标记字段与其他字段时,会用到自旋锁。例如refcount和usage_count的自增和自减。会先获取自旋锁,然后自增或自减,再释放自旋锁。同样在脏位设置时,也会用到自旋锁。

9.6之后使用原子操作替换了bufferdesc结构体的很多字段的功能。

refcount、usagecount标记位组合起来放入一个原子变量state字段中,而缓存区首部锁实际上是嵌入标记位中为一个bit,这种设计允许我们使用单个原子操作,而不是获取/释放自旋锁来实现一些操作。比如refcount的自增和自减,脏位的设置。

源码如下:

/*
 * Buffer state is a single 32-bit variable where following data is combined.
 *
 * - 18 bits refcount
 * - 4 bits usage count
 * - 10 bits of flags
 *
 * Combining these values allows to perform some operations without locking
 * the buffer header, by modifying them together with a CAS loop.
 *
 * The definition of buffer state components is below.
 */
#define BUF_REFCOUNT_ONE 1
#define BUF_REFCOUNT_MASK ((1U << 18) - 1)
#define BUF_USAGECOUNT_MASK 0x003C0000U
#define BUF_USAGECOUNT_ONE (1U << 18)
#define BUF_USAGECOUNT_SHIFT 18
#define BUF_FLAG_MASK 0xFFC00000U

/* Get refcount and usagecount from buffer state */
#define BUF_STATE_GET_REFCOUNT(state) ((state) & BUF_REFCOUNT_MASK)
#define BUF_STATE_GET_USAGECOUNT(state) (((state) & BUF_USAGECOUNT_MASK) >> BUF_USAGECOUNT_SHIFT)

/*
 * Flags for buffer descriptors
 *
 * Note: BM_TAG_VALID essentially means that there is a buffer hashtable
 * entry associated with the buffer's tag.
 */
#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) */
/*
 * The maximum allowed value of usage_count represents a tradeoff between
 * accuracy and speed of the clock-sweep buffer management algorithm.  A
 * large value (comparable to NBuffers) would approximate LRU semantics.
 * But it can take as many as BM_MAX_USAGE_COUNT+1 complete cycles of
 * clock sweeps to find a free buffer, so in practice we don't want the
 * value to be very large.
 */
#define BM_MAX_USAGE_COUNT      5
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南風_入弦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值