概述
常规锁是数据库中实现的锁,它和自旋锁及轻量锁不同,自旋锁和轻量锁属于系统锁,主要用于保护数据库系统中的一些关键变量,服务于数据库内核的实现;常规锁属于事务锁,主要用于协调各种不同事务对数据库对象如表、页、元组等的并发访问。
内存结构
锁模式
常规锁根据其不同的使用场景,分为了8中锁模式,每种模式权限不同,这样就可以在不同的场景下通过申请不同模式的锁,从而控制锁的权限。
- AccessShareLock
访问共享锁,查询时会自动施加到被查询的表上。 - RowShareLock
行共享锁,当SQL语句中采用了select…for update 和for share语句时将使用行共享锁对表加锁。 - RowExclusiveLock
行排他锁,使用IUD语句时将使用行排它锁对表加锁,但只是一个意向锁,表明有事务对该表尽显IUD操作而已,并不会真正对行加锁,行锁另有其他的实现方法。 - ShareUpdateExclusiveLock
共享更新排它锁,使用vacuum或者create index concurrently语句时使用该锁。 - ShareLock
共享锁,使用不带concurrently选项的create index语句请求时用共享锁对表加锁。 - ShareRowExclusiveLock
共享行排它锁,类似与排它锁,但是允许行共享。任何数据库命令都不会请求这个锁,除了lock命令。如,lock table tb1 in share row exclusive mode - ExclusiveLock
排它锁,阻塞行共享和select…for update语句。数据库中不会再用户表上自动请求这个锁,但是再系统表的可能会请求这个锁。 - AccessExclusiveLock
访问排它锁,执行alter table, truncate ,reindex,cluster,drop table 以及vacuum full等SQL命令时会添加该锁,是最高级别的锁。
锁的相容性矩阵
常用锁结构
常规锁主要保存在4个位置:
- 主锁表: 在共享内存中,保存了一个锁对象的所有相关信息
- 进程锁表:在共享内存中,保存了一个锁对象与当前进程的所有相关信息
- 快速路径:在当前进程中,保存了弱锁的信息,避免频繁重复访问主锁表和进程锁表。
从上面的锁模式相容性矩阵可以看出,AccessShareLock、RowShareLock、RowExclusiveLock这三个锁模式是互不冲突的,而且常用于DML操作,因此成为弱锁,剩余5个锁则为强锁。弱锁之间互不冲突,强锁和弱锁冲突。 - 本地锁表:保存在当前进程中,对重复申请的锁进行计数,相当与本地缓存,避免重复访问主锁表和进程锁表。
所以申请锁时从上述4个位置查找的流程为:本地锁表–>快速路径–>进程锁表=主锁表,有三个全局变量控制,是哈希表
static HTAB *LockMethodLockHash;//本地锁表
static HTAB *LockMethodProcLockHash;//进程锁表
static HTAB *LockMethodLocalHash;//主锁表
结构体
锁相关的结构体主要就是上述几种类型,如下图
Lock
Lock结构体记录了主锁表中锁对象的所有相关信息,比如当前持有锁的信息,等待锁的信息,关联的进程等信息。
typedef enum LockTagType
{LOCKTAG_RELATION, /* 表锁 */LOCKTAG_RELATION_EXTEND, /* 对表进行扩展时加的锁*/LOCKTAG_DATABASE_FROZEN_IDS, /* 数据库冻结ID时的锁 */LOCKTAG_PAGE, /* 页锁 */LOCKTAG_TUPLE, /* 行锁 */LOCKTAG_TRANSACTION, /* 事务锁*/LOCKTAG_VIRTUALTRANSACTION, /* 虚拟事务锁 */LOCKTAG_SPECULATIVE_TOKEN, /* UPSERT语句中需要等待confirm的元组加锁*/LOCKTAG_OBJECT, /* 非表对象锁*/LOCKTAG_USERLOCK, /* 用户锁 */LOCKTAG_ADVISORY /* 咨询锁*/
} LockTagType;typedef struct LOCKTAG
{uint32 locktag_field1; /* dboid */uint32 locktag_field2; /* reloid */uint32 locktag_field3; /* blocknum */uint16 locktag_field4; /* forknum */uint8 locktag_type; /* 锁类型,参见LockTagType类型 */uint8 locktag_lockmethodid; /* lockmethod indicator: 锁方法,目前就两种:默认锁方法和用户锁方法 */
} LOCKTAG;typedef struct LOCK
{/* hash key */LOCKTAG tag; /* 标识符,全局唯一*//* data */LOCKMASK grantMask; /* 已经被持有的锁的掩码 */LOCKMASK waitMask; /* 正在等待的锁的掩码 */SHM_QUEUE procLocks; /* 当前锁相关联的进程锁表 */PROC_QUEUE waitProcs; /* 正在等待当前锁的进程锁表 */int requested[MAX_LOCKMODES]; /* 需要当前锁的会话的数量 */int nRequested; /* 需索当前锁的所有会话数量 */int granted[MAX_LOCKMODES]; /* 已经持有当前锁的会话数 */int nGranted; /* 已经持有当前锁的所有会话的数量 */
} LOCK;
LocalLock
本地锁表就是锁对象在本地进程的缓存,进程第一次申请一个锁对象后会将其放到本地锁表中,后面若是需要再申请该锁,则直接从本地锁表中获取即可,不需要再去主锁表申请,能大大提升性能。
typedef struct LOCALLOCKTAG
{LOCKTAG lock; /* 本地锁表的标识符*/LOCKMODE mode; /* 表的锁模式 */
} LOCALLOCKTAG;//本地锁表类型
typedef struct LOCALLOCK
{/* tag */LOCALLOCKTAG tag; /* 锁标识符: *//* data */uint32 hashcode; /* 锁标识符的哈希值: */LOCK *lock; /* 锁对象:*/PROCLOCK *proclock; /* 进程锁表对象 */int64 nLocks; /* 锁被持有的最大次数 */int numLockOwners; /* 相关的resourceOwner数量: */int maxLockOwners; /* 分匹配的数组大小 */LOCALLOCKOWNER *lockOwners; /* 动态再分配*/bool holdsStrongLockCount; /* 是否持有强锁 */bool lockCleared; /* 锁是否已清理 */
} LOCALLOCK;
ProcLock
PROCLOCK结构记录进程锁表所有相关信息,它是锁对象与进程连接的纽带,这样就能通过Lock锁对象查找申请该锁对应的进程信息,也能通过Proc进程对象查找其申请的锁的信息。
typedef struct PROCLOCKTAG
{/* NB: we assume this struct contains no padding! */LOCK *myLock; /* 关联的锁的信息 */PGPROC *myProc; /* 关联的进程的信息 */
} PROCLOCKTAG;//进程锁表,每个需求锁的信息
typedef struct PROCLOCK
{/* tag */PROCLOCKTAG tag; /* 进程锁表标识,全局唯一 *//* data */PGPROC *groupLeader; /* 每个锁组的组长信息 */LOCKMASK holdMask; /* 当前持有锁的类型的掩码信息*/LOCKMASK releaseMask; /* 要释放的锁类型的掩码 */SHM_QUEUE lockLink; /* 进程锁表的锁链表*/SHM_QUEUE procLink; /* 进程锁表的进程链表*/
} PROCLOCK;
相关函数
LockAcquireExtended
该函数的作用就是申请一把常规锁。其申请流程就是判断申请的锁的位置,然后申请,先尝试从本地锁表中申请,然后尝试从快速路径中申请,最后是从主锁表和进程锁表申请。
从本地锁表申请
本地锁表是由一个全局变量LockMethodLocalHash记录,这是一个哈希表,申请本地锁表会根据申请的锁的类型和信息从LockMethodLocalHash中进行查找,并返回查找结果。
如果找到则增加锁计数后直接返回申请结果。如果本地锁表已经没有空间保存申请的锁的LockOwner信息,则扩容一倍。
如果没有找到,初始化一个空的本地锁表结构,后面在其他位置申请到后直接保存到本地锁表中。
//去本地锁表查询,是一个哈希表,找到的话,返回找到的本地锁对象locallock = (LOCALLOCK *) hash_search(LockMethodLocalHash,(void *) &localtag,HASH_ENTER, &found);if (!found)//没有找到,先本地初始化一个空的本地锁表对象,后面从其他地方找到后会先放入本地锁表中{locallock->lock = NULL;locallock->proclock = NULL;locallock->hashcode = LockTagHashCode(&(localtag.lock));locallock->nLocks = 0;locallock->holdsStrongLockCount = false;locallock->lockCleared = false;locallock->numLockOwners = 0;locallock->maxLockOwners = 8;locallock->lockOwners = NULL;locallock->lockOwners = (LOCALLOCKOWNER *)MemoryContextAlloc(TopMemoryContext,locallock->maxLockOwners * sizeof(LOCALLOCKOWNER));}else//本地锁表找到了{if (locallock->numLockOwners >= locallock->maxLockOwners)//如果持有该锁的用户大于限制数量,再扩大一倍{int newsize = locallock->maxLockOwners * 2;locallock->lockOwners = (LOCALLOCKOWNER *)repalloc(locallock->lockOwners,newsize * sizeof(LOCALLOCKOWNER));locallock->maxLockOwners = newsize;}}hashcode = locallock->hashcode;if (locallockp)*locallockp = locallock;//保存找到的本地锁if (locallock->nLocks > 0){GrantLockLocal(locallock, owner);//增加锁计数if (locallock->lockCleared) //返回结果return LOCKACQUIRE_ALREADY_CLEAR;elsereturn LOCKACQUIRE_ALREADY_HELD;}
从快速路径申请
从快速路径申请,分为申请弱锁和强锁。
本地进程最多保存16把弱锁,这是因为本地进程PGPROC->fpRelId是一个长度为16的数组,它里面保存的是本地进程只有锁的表的OID信息,这就限制了弱锁的数量。
- 申请弱锁
- 判断是否为弱锁,其判断条件为:
- 锁类型是表锁,锁模式<4,即前三种锁,通过EligibleForRelationFastPath宏判断
#define EligibleForRelationFastPath(locktag, mode) \
(locktag)->locktag_type == LOCKTAG_RELATION &&
(locktag)->locktag_field1 == MyDatabaseId &&
MyDatabaseId != InvalidOid &&
(mode) < ShareUpdateExclusiveLock)
```
2. 本地保存的快速路径锁数量小于16- 获取强锁的哈希码
- 判断强锁是否存在,根据全局变量FastPathStrongRelationLocks来判断,如果存在,则申请失败
- 如果强锁不存在,则申请弱锁
- 申请到弱锁,更新锁计数并返回
- 判断是否为弱锁,其判断条件为:
if (EligibleForRelationFastPath(locktag, lockmode) &&FastPathLocalUseCount < FP_LOCK_SLOTS_PER_BACKEND)//判断是否为弱锁:最大可持有16个 {uint32 fasthashcode = FastPathStrongLockHashPartition(hashcode);//获取强锁的哈希码bool acquired;LWLockAcquire(&MyProc->fpInfoLock, LW_EXCLUSIVE);if (FastPathStrongRelationLocks->count[fasthashcode] != 0)//判断该强锁是否已存在,若已存在则申请失败acquired = false;elseacquired = FastPathGrantRelationLock(locktag->locktag_field2,lockmode);//申请弱锁LWLockRelease(&MyProc->fpInfoLock);if (acquired)//申请到弱锁,直接返回{locallock->lock = NULL;locallock->proclock = NULL;GrantLockLocal(locallock, owner);//增加锁计数return LOCKACQUIRE_OK;}}
- 申请强锁
- 判断是否为强锁
判断锁模式等信息,根据ConflictsWithRelationFastPath宏判断
- 判断是否为强锁
#define ConflictsWithRelationFastPath(locktag, mode) \((locktag)->locktag_lockmethodid == DEFAULT_LOCKMETHOD && \(locktag)->locktag_type == LOCKTAG_RELATION && \(locktag)->locktag_field1 != InvalidOid && \(mode) > ShareUpdateExclusiveLock)//判断是否为强锁
- 获取强锁哈希码
- 申请强锁
- 如果申请到强锁,则将其他进程保存的该锁的弱锁转移到主锁表中
if (ConflictsWithRelationFastPath(locktag, lockmode)){uint32 fasthashcode = FastPathStrongLockHashPartition(hashcode);//获取哈希码BeginStrongLockAcquire(locallock, fasthashcode);//申请强锁//将其他事务保存的弱锁保存到主锁表中,因为有了强锁之后,强锁与弱锁就冲突了if (!FastPathTransferRelationLocks(lockMethodTable, locktag,hashcode)){AbortStrongLockAcquire();//终止获取强锁if (locallock->nLocks == 0)RemoveLocalLock(locallock); //如果引用计数为0,就删除本地锁elsereturn LOCKACQUIRE_NOT_AVAIL;}}
从主锁表申请
如果本地锁表和快速路径都没申请到锁,则需要去主锁表中申请了,调用SetupLockInTable函数申请,申请到之后,还需要进行锁冲突检测,如果没有冲突,就可以直接获取这个锁了,但是如果有锁冲突,就需要将自己放到等待队列上等待了。主要流程如下:
- 先申请主锁表的轻量锁MainLWLockArray,因为要访问共享内存,以排他模式申请
partitionLock = LockHashPartitionLock(hashcode);LWLockAcquire(partitionLock, LW_EXCLUSIVE);
- 调用SetupLockInTable函数申请,该函数会先去主锁表和进程锁表中查找要申请的锁,如果没有找到,则需要申请内存来保存申请的锁的信息。
proclock = SetupLockInTable(lockMethodTable, MyProc, locktag,hashcode, lockmode);//去主锁表中寻找if (!proclock)return LOCKACQUIRE_NOT_AVAIL;
- 将申请到锁保存到本地锁表中,下一次再申请就可以直接从本地锁表中申请了
locallock->proclock = proclock;lock = proclock->tag.myLock;locallock->lock = lock;
- 检查锁是否与其他锁冲突,如果没有冲突更新锁计数
if (lockMethodTable->conflictTab[lockmode] & lock->waitMask)found_conflict = true;elsefound_conflict = LockCheckConflicts(lockMethodTable, lockmode,lock, proclock);if (!found_conflict){GrantLock(lock, proclock, lockmode);GrantLockLocal(locallock, owner);
- 如果锁冲突,但不需要等待,则删除当前记录的锁信息,直接返回获取失败,是否需要等待由传入的参数dontWait确定
if (dontWait){AbortStrongLockAcquire();if (proclock->holdMask == 0){uint32 proclock_hashcode;proclock_hashcode = ProcLockHashCode(&proclock->tag, hashcode);SHMQueueDelete(&proclock->lockLink);SHMQueueDelete(&proclock->procLink);}lock->nRequested--;lock->requested[lockmode]--;LOCK_PRINT("LockAcquire: conditional lock failed", lock, lockmode);Assert((lock->nRequested > 0) && (lock->requested[lockmode] >= 0));Assert(lock->nGranted <= lock->nRequested);LWLockRelease(partitionLock);if (locallock->nLocks == 0)RemoveLocalLock(locallock);if (locallockp)*locallockp = NULL;return LOCKACQUIRE_NOT_AVAIL;}
- 如果锁冲突,且需要等待,调用WaitOnLock函数等待锁的释放。
MyProc->heldLocks = proclock->holdMask;
WaitOnLock(locallock, owner);//不能获取到锁,进入等待队列等待
- 释放MainLWLockArray锁
LWLockRelease(partitionLock);
SetupLockInTable
从主锁表和进程锁表中查找一个锁对象,如果找不到就创建一个新的。
- 查询主锁表,从全局变量LockMethodLockHash中查询
lock = (LOCK *) hash_search_with_hash_value(LockMethodLockHash,(const void *) locktag,hashcode,HASH_ENTER_NULL,&found);
- 没查到,创建新的锁对象
if (!found)//没有找到,创建一个新的锁对象{lock->grantMask = 0;lock->waitMask = 0;SHMQueueInit(&(lock->procLocks));ProcQueueInit(&(lock->waitProcs));lock->nRequested = 0;lock->nGranted = 0;MemSet(lock->requested, 0, sizeof(int) * MAX_LOCKMODES);MemSet(lock->granted, 0, sizeof(int) * MAX_LOCKMODES);LOCK_PRINT("LockAcquire: new", lock, lockmode);
- 查询进程锁表,从全局变量LockMethodProcLockHash中查询
proclocktag.myLock = lock;//获取进程锁表的tag信息proclocktag.myProc = proc;proclock_hashcode = ProcLockHashCode(&proclocktag, hashcode);proclock = (PROCLOCK *) hash_search_with_hash_value(LockMethodProcLockHash,(void *) &proclocktag,proclock_hashcode,HASH_ENTER_NULL,&found);
- 没查到,创建新的进程锁表对象
if (!found)//如果没找到就是创建一个新的进程锁表{uint32 partition = LockHashPartition(hashcode);proclock->groupLeader = proc->lockGroupLeader != NULL ?proc->lockGroupLeader : proc;proclock->holdMask = 0;proclock->releaseMask = 0;SHMQueueInsertBefore(&lock->procLocks, &proclock->lockLink);SHMQueueInsertBefore(&(proc->myProcLocks[partition]),&proclock->procLink);PROCLOCK_PRINT("LockAcquire: new", proclock);}
LockCheckConflicts
检查当前要申请的锁是否与其他的进程持有的锁冲突,主要通过主锁表中的conflicttab的掩码值与要申请的锁的掩码进行比较得出结果。如果存在多进程的情况(即group),还需要排除掉同group的冲突锁,最终得出是否冲突的结果。
- 判断要申请的锁是否与主锁表中的其他锁冲突,没有冲突直接返回
if (!(conflictMask & lock->grantMask))//判断当前锁模式掩码与冲突的掩码。得出是否存在冲突{PROCLOCK_PRINT("LockCheckConflicts: no conflict", proclock);return false;//都不冲突}
- 如果存在冲突,遍历所有的锁模式,判断冲突的锁进程的数量,去掉当前进程,如果数量为0,表示为没有冲突
myLocks = proclock->holdMask;for (i = 1; i <= numLockModes; i++)//遍历所有锁模式{if ((conflictMask & LOCKBIT_ON(i)) == 0)//指定的锁模式上不存在冲突{conflictsRemaining[i] = 0;continue;}conflictsRemaining[i] = lock->granted[i];//第i个模式在Lock上被授予了多少次了if (myLocks & LOCKBIT_ON(i))--conflictsRemaining[i];//是当前进程持有的锁,不算冲突totalConflictsRemaining += conflictsRemaining[i];//统计有多少个事务持有该锁,不包括自己}/* If no conflicts remain, we get the lock. */if (totalConflictsRemaining == 0)//没有冲突的锁,那么可以直接获取该锁{PROCLOCK_PRINT("LockCheckConflicts: resolved (simple)", proclock);return false;}
- 如果数量不为0,且没有group,那么存在冲突
//如果有冲突的锁,且Groupleader是自己即单进程,则有冲突if (proclock->groupLeader == MyProc && MyProc->lockGroupLeader == NULL){Assert(proclock->tag.myProc == MyProc);PROCLOCK_PRINT("LockCheckConflicts: conflicting (simple)",proclock);return true;}
- 如果存在group,则遍历每个进程,减去与当前进程同一个group的进程,得到剩余的总数量,如果为0,不存在冲突,否则存在冲突
procLocks = &(lock->procLocks);otherproclock = (PROCLOCK *)SHMQueueNext(procLocks, procLocks, offsetof(PROCLOCK, lockLink));//遍历所有其他的进程,减去与当前进程一个group的冲突的锁,减完如果为0就没有冲突,否则就有冲突while (otherproclock != NULL){if (proclock != otherproclock &&proclock->groupLeader == otherproclock->groupLeader &&(otherproclock->holdMask & conflictMask) != 0)//其他的进程且与当前进程在一个group中,且存在锁冲突{int intersectMask = otherproclock->holdMask & conflictMask;//是否冲突for (i = 1; i <= numLockModes; i++)//遍历所有锁模式{if ((intersectMask & LOCKBIT_ON(i)) != 0)//冲突{conflictsRemaining[i]--;//因为是同组的,所以冲突的就不是冲突了,减掉totalConflictsRemaining--;}}if (totalConflictsRemaining == 0)//所有冲突的锁都符合,那么就不存在锁冲突了{PROCLOCK_PRINT("LockCheckConflicts: resolved (group)",proclock);return false;}}otherproclock = (PROCLOCK *)SHMQueueNext(procLocks, &otherproclock->lockLink,offsetof(PROCLOCK, lockLink));//遍历下一个}
FastPathGrantRelationLock
申请快速路径的弱锁,每个进程最多能持有16个弱锁,所以预留的槽位只有16个,遍历 每个槽位,找到没被占用的槽位,然后将要申请的锁信息填入该槽位占住即可。
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++)//遍历每一个槽位,通16个槽位{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;
}
BeginStrongLockAcquire
申请强锁,就是更新全局变量FastPathStrongRelationLocks的值,然后将本地锁表的holdsStrongLockCount设置为true
BeginStrongLockAcquire(LOCALLOCK *locallock, uint32 fasthashcode)
{SpinLockAcquire(&FastPathStrongRelationLocks->mutex);//修改全局变量,先申请锁FastPathStrongRelationLocks->count[fasthashcode]++;//更新对应的强锁计数locallock->holdsStrongLockCount = true;//标记一下,本地锁表申请的有强锁StrongLockInProgress = locallock;//当前的锁是强锁SpinLockRelease(&FastPathStrongRelationLocks->mutex);//释放锁
}
FastPathTransferRelationLocks
获取到强锁以后,如果其他进程的快速路径上还保存有对应的弱锁,就需要将这些弱锁挪到主锁表中,否则这些快速路径上的弱锁将因为与强锁冲突而失效。
该函数会遍历所有的进程,所有的进程保存在全局变量ProcGlobal->allProcs中,然后查找对应的弱锁,找到后挪到主锁表中,主要流程如下:
- 遍历每个进程,如果跟持锁的数据库信息不一致,直接跳过
- 遍历每个进程上的弱锁的槽位(16个),弱锁相关的表ID跟申请的不一致或对应槽位未被使用,直接跳过
- 遍历每个弱锁模式(3个),若该模式弱锁不存在,直接跳过
- 走到这里,就说明该进程的某个槽位存在弱锁且与申请的强锁冲突,去主锁表中重新创建该锁(即将该弱锁转移到主锁表中)。
- 清空该进程上相关的弱锁信息
FastPathTransferRelationLocks(LockMethod lockMethodTable, const LOCKTAG *locktag,uint32 hashcode)
{LWLock *partitionLock = LockHashPartitionLock(hashcode);Oid relid = locktag->locktag_field2;uint32 i;for (i = 0; i < ProcGlobal->allProcCount; i++)//遍历所有的进程{PGPROC *proc = &ProcGlobal->allProcs[i];//取一个进程uint32 f;LWLockAcquire(&proc->fpInfoLock, LW_EXCLUSIVE);//获取该进程上的快速路径锁,锁住后禁止其他进程访问该进程相关的快速路径的信息if (proc->databaseId != locktag->locktag_field1)//如果不然会同一个数据库,可以直接跳过,因为不相关{LWLockRelease(&proc->fpInfoLock);continue;}for (f = 0; f < FP_LOCK_SLOTS_PER_BACKEND; f++)//遍历进程上的所有弱锁槽位{uint32 lockmode;/* Look for an allocated slot matching the given relid. */if (relid != proc->fpRelId[f] || FAST_PATH_GET_BITS(proc, f) == 0)//表ID或持锁信息不符合要求,跳过continue;/* Find or create lock object. */LWLockAcquire(partitionLock, LW_EXCLUSIVE);//主锁表加锁for (lockmode = FAST_PATH_LOCKNUMBER_OFFSET;lockmode < FAST_PATH_LOCKNUMBER_OFFSET + FAST_PATH_BITS_PER_SLOT;++lockmode)//遍历每种弱锁模式{PROCLOCK *proclock;if (!FAST_PATH_CHECK_LOCKMODE(proc, f, lockmode))//不存在弱锁,跳过continue;proclock = SetupLockInTable(lockMethodTable, proc, locktag,hashcode, lockmode);//在主锁表中创建或申请对应的锁if (!proclock){LWLockRelease(partitionLock);LWLockRelease(&proc->fpInfoLock);return false;}GrantLock(proclock->tag.myLock, proclock, lockmode);//更新锁计数FAST_PATH_CLEAR_LOCKMODE(proc, f, lockmode);//清空该进程上的弱锁计数}LWLockRelease(partitionLock);/* No need to examine remaining slots. */break;}LWLockRelease(&proc->fpInfoLock);}return true;
}
WaitOnLock
当前进程睡眠并等待其期望的锁释放,调用ProcSleep函数睡眠,ProcSleep函数中又会调用CheckDeadLock函数进行死锁检测。
【参考】
- 《PostgreSQL数据库内核分析》
- 《Postgresql技术内幕-事务处理深度探索》
- 《PostgreSQL指南:内幕探索》
- pg14源码