LWLock in postgres

一 相关结构体说明

1
2
3
4
5
6
7
8
9
10
typedef struct LWLock
{
uint16 tranche; /* tranche ID */
pg_atomic_uint32 state; /* state of exclusive/nonexclusive lockers */
proclist_head waiters; /* list of waiting PGPROCs */
#ifdef LOCK_DEBUG
pg_atomic_uint32 nwaiters; /* number of waiters */
struct PGPROC *owner; /* last exclusive owner of the lock */
#endif
} LWLock;
  • tranche
    每个 LWLock 都属于某个 tranche,tranche 是一个 int 类型的 ID,代表这把锁的用途

  • state (不同版本可能会有变化,本文对应版本:PostgreSQL 19devel(开发版,尚未正式发布的 19))
    bit0..17:共享持有者计数(一个整数计数,不是位图)
    bit18:独占锁哨兵位(LW_VAL_EXCLUSIVE = 1<<18)

bit29: LW_FLAG_LOCKED,是 wait list 自身的小锁(保护等待队列操作)
bit30: LW_FLAG_RELEASE_OK,表示当前可以在释放路径执行唤醒
bit31: LW_FLAG_HAS_WAITERS,表示等待队列里有 waiter

  • waiters
    等待队列

二 Interface说明

2.1 LWLockAcquire

加锁,失败进入等待队列,直接加上锁的情况,可能会造成等待队列无效唤醒

acquire a lightweight lock in the specified mode
Side effect: cancel/die interrupts are held off until lock release.

1
2
3
4
5
6
7
8
9
10
11
12
if (mode == LW_EXCLUSIVE)
{
lock_free = (old_state & LW_LOCK_MASK) == 0;
if (lock_free)
desired_state += LW_VAL_EXCLUSIVE;
}
else
{
lock_free = (old_state & LW_VAL_EXCLUSIVE) == 0;
if (lock_free)
desired_state += LW_VAL_SHARED;
}

上述代码确认是否可以加锁

2.2 LWLockRelease

进行唤醒条件:

  • oldstate & LW_FLAG_HAS_WAITERS:等待队列非空。这个条件确认有进程在等待锁。
  • oldstate & LW_FLAG_RELEASE_OK: 允许唤醒。这个标志表示系统在上次唤醒后,已经将再次唤醒其他等待者的权限交给了被唤醒的进程。
  • (oldstate & LW_LOCK_MASK) == 0:锁已空闲。这个条件确认锁当前没有被任何进程占用(无论是独占模式还是共享模式,计数都为零)

三 分配与类型(默认值,版本相关)

  1. NUM_INDIVIDUAL_LWLOCKS:核心按表生成的单锁(lwlocklist.h),数量随版本变化,不是固定 56。
  2. NUM_BUFFER_PARTITIONS(默认 128):共享缓冲区 mapping hash 的分区锁 BufferMappingLock[i]
  3. NUM_LOCK_PARTITIONS(默认 16):heavyweight lock manager 哈希分区锁(LWTRANCHE_LOCK_MANAGER)。
  4. NUM_PREDICATELOCK_PARTITIONS(默认 16):谓词锁哈希分区锁。
  5. 扩展自定义:在 postmaster 启动阶段调用 RequestNamedLWLockTranche(name, n) 申请一组锁,重启后 GetNamedLWLockTranche 取基址。需要 shared_preload_libraries 的扩展通常属于此类。