PostgreSQL数据库锁机制——FastPathStrongRelationLocks简介

快路径强关闭表锁 static volatile FastPathStrongRelationLockData *FastPathStrongRelationLocks
快路径强关闭表锁,用来记录是否有事务已经获得这个对象的强锁。从锁的相容性矩阵可以看出,AccessShareLock、RowShareLock、RowExclusiveLock这3个锁是不互相冲突的,是相容的,而这几个锁主要用于事务的DML,因此可以把这几个锁定义为弱锁,而其他5个锁则定义为强锁。快路径强关闭表锁是一个FastPathStrongRelationLockData的结构体。其包含了一个mutex用作SpinLock。

typedef struct {
	slock_t		mutex; // 保护这个结构的一个自旋锁
	uint32		count[FAST_PATH_STRONG_LOCK_HASH_PARTITIONS]; // 计数标记,如果有人申请了强锁,计数加1
} FastPathStrongRelationLockData;
static volatile FastPathStrongRelationLockData *FastPathStrongRelationLocks;
#define FAST_PATH_STRONG_LOCK_HASH_BITS			10
#define FAST_PATH_STRONG_LOCK_HASH_PARTITIONS (1 << FAST_PATH_STRONG_LOCK_HASH_BITS) // FAST_PATH_STRONG_LOCK_HASH_PARTITIONS为1<<10
#define FastPathStrongLockHashPartition(hashcode) ((hashcode) % FAST_PATH_STRONG_LOCK_HASH_PARTITIONS) // 获取count计数标记数组的索引

如果一个事务对某个对象申请弱锁(基于申请弱锁的概率比申请强锁高,优化申请弱锁符合奥海姆剃刀原则),同时能够查阅到其他事务在这个对象上没有申请过强锁,则可以在事务所在的会话上记录这个弱锁(会话持有弱锁,由于其相容性,它们之间不会互相影响),而不必把这个弱锁保存到共享内存(主锁表)。那么一个事务如何知道其他事务是否在一个对象上申请了强锁?如果一个事务在某个对象上申请了强锁,则会在共享内存中做一个标识,这样当一个事务申请弱锁时就会出现以下两种情况。

  • 如果其他事务已经获得了这个对象的强锁,则本事务不会使用锁的Fast Path,它会按照常规的方法将锁保存到主锁表
  • 如果没有其他事务获得到这个对象的强锁,则本事务将本次的弱锁保存到本会话,也就是进入Fast Path模式。
    由于DML操作是常规操作,DDL操作或DCL操作的频率不高,那么数据库大部分时间都只会使用弱锁,这就避免了频繁访问主锁表,只将弱锁保存到当前会话,提高了数据库的性能(虽然判断是否有强锁也需要访问共享内存中FastPathStrongRelationLocks中的标记,但这种访问的粒度比较小)。

操作

EligibleForRelationFastPath判断该次申请locktag和lockmode是否能应用FastPathStrongRelationLocks优化。

EligibleForRelationFastPath(locktag, lockmode)
#define EligibleForRelationFastPath(locktag, mode) \
	((locktag)->locktag_lockmethodid == DEFAULT_LOCKMETHOD && (locktag)->locktag_type == LOCKTAG_RELATION && (locktag)->locktag_field1 == MyDatabaseId && MyDatabaseId != InvalidOid && (mode) < ShareUpdateExclusiveLock)

FastPathStrongLockHashPartition所需的hashcode是存在本地锁表中的成员locallock->hashcode。

#define FastPathStrongLockHashPartition(hashcode) ((hashcode) % FAST_PATH_STRONG_LOCK_HASH_PARTITIONS) // 获取count计数标记数组的索引

如果count计数标记数组的索引所存放的计数不等于0,则表示已经有事务在对象上申请了强锁;否则直接调用FastPathGrantRelationLock进行弱锁申请。

		LWLockAcquire(&MyProc->backendLock, LW_EXCLUSIVE);
		if (FastPathStrongRelationLocks->count[fasthashcode] != 0)
			acquired = false;
		else
			acquired = FastPathGrantRelationLock(locktag->locktag_field2, lockmode);
		LWLockRelease(&MyProc->backendLock);

FastPathGrantRelationLock函数使用后端进程本地的fast-path array Grant lock。

static bool FastPathGrantRelationLock(Oid relid, LOCKMODE lockmode) {
	uint32		f;
	uint32		unused_slot = FP_LOCK_SLOTS_PER_BACKEND;
	/* Scan for existing entry for this relid, remembering empty slot. */
	for (f = 0; f < FP_LOCK_SLOTS_PER_BACKEND; f++) {
		if (FAST_PATH_GET_BITS(MyProc, f) == 0) unused_slot = f;
		else if (MyProc->fpRelId[f] == relid) {
			Assert(!FAST_PATH_CHECK_LOCKMODE(MyProc, f, lockmode));
			FAST_PATH_SET_LOCKMODE(MyProc, f, lockmode);
			return true;
		}
	}
	/* If no existing entry, use any empty slot. */
	if (unused_slot < FP_LOCK_SLOTS_PER_BACKEND) {
		MyProc->fpRelId[unused_slot] = relid;
		FAST_PATH_SET_LOCKMODE(MyProc, unused_slot, lockmode);
		++FastPathLocalUseCount;
		return true;
	}
	/* No existing entry, and no empty slot. */
	return false;
}
上一篇:postgresql之性能优化


下一篇:CentOS7安装PostgreSQL