postgresql 的架构是基于多进程的,进程之间往往存在着数据共享,比如缓存读写。这时 postgresql 需要提高一种锁机制来支持并发访问,但是 linux 只实现了线程间的读写锁,所以 postgresql 自己实现进程间的读写锁,它主要利用了信号量和自旋锁来实现。
每个进程都对应一个独立 实例,它包含了多个成员,下面只列出与锁相关的
实例存储在进程共享内存里,在创建进程时会被初始化。这里需要提醒下,信号量 sem 的初始值为0,属于无名信号量。
结构体表示读写锁,结构如下:
需要介绍下 state 字段的标记位
加锁原理
函数负责整个加过程,这里将过程分为下面四步:
从上面可以看出来,这里采用的是非公平锁机制,也就是谁的速度快,谁就能获取到锁,没有先来先获取的顺序。
尝试通过 CAS 操作,设置或标记位。来获取锁。使用到的 CAS 操作函数如下:
当尝试获取读锁时,只需要检查标记位。如果为零,就认为成功获取。
对于队列的操作,都需要使用自旋锁来设置 标记位,才有权利操作队列。当设置成功后,会将自身添加到等待列表,然后更新自身进程对应实例的 和 成员,最后释放锁,清除标记位。
当加入到队列后,还需要更新标记位,表示有进程在等待。
postgresql 是多进程架构的,进程之间使用信号量来同步。当进程获取锁失败时,会将自身添加到等待队列里,然后通过信号量进行阻塞,直到被别的进程唤醒。对于 postgresql 的每个进程都有一个信号量,由实例的 sem 成员表示。sem 信号量的初始值为0,它的数值代表着满足的条件数。
因为这个 sem 信号量会有多个用途,所以每次唤醒操作,并不一定是期望的条件发生,所以进程在被唤醒之后,需要一次条件检查。如果是因为其他条件唤醒,还需要记录被唤醒次数,在事后需要将执行相应次数的唤醒操作。
假设下面有一个进程A对应信号量A,它会被用于两个方面,条件A和条件B。
进程A正在阻塞条件A,此时进程B因为条件B唤醒了进程A。但进程A发现并不是期望的条件所唤醒的,所以它会继续阻塞,直到满足条件A被唤醒,最后进程A还会执行一次唤醒操作。

当获取到锁之后,如果自身在等待队列中,需要将其删除掉。如果之后队列为空,需要清除标记位,当然这些操作都需要获取锁。
下面展示了的源码,描述了整个加锁过程
释放锁
函数负责释放锁,定义如下:
释放锁分为下列步骤:
下面列举了两种情况来阐述锁释放的原理:
在取出进程之后,会清除标记位。如果检查到队列为空,还会清除标记位。
lwlock 在 postgresql 中使用的场景非常多,为了更好管理这些 lwlock,postgresql 按照用途将它们分类,大体分为三个部分:

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/201768.html