赞
踩
在数据库启动阶段,pg通过InitLocks函数初始化保存锁对象的共享内存空间。在共享内存中,有两个“锁表”用于保存锁对象,分别是:
主锁表用于保存当前数据库中所有事务的锁对象,即LOCK结构体。
- typedef struct LOCK
- {
- /* hash key */
- LOCKTAG tag; /* 锁对象的唯一ID */
-
- /* data */
- LOCKMASK grantMask; /* 该对象已经持有的锁模式 */
- LOCKMASK waitMask; /* 等待队列中请求该对象的锁模式 */
- SHM_QUEUE procLocks; /* 这个锁上的所有 PROCLOCK */
- PROC_QUEUE waitProcs; /* 等在这个锁上的 PROCLOCK */
- int requested[MAX_LOCKMODES]; /* 请求(持有+等待)该锁的会话数 */
- int nRequested; /* requested数组的元素数 */
- int granted[MAX_LOCKMODES]; /* 持有该锁的会话数 */
- int nGranted; /* granted数组的元素数 */
- } LOCK;
进程锁表保存当前进程(会话)的事务锁状态,即PROCLOCK结构体,这个结构体主要用于建立锁和会话的关系。
- typedef struct PROCLOCKTAG
- {
- /* NB: we assume this struct contains no padding! */
- LOCK *myLock; /* link to per-lockable-object information,指向每个锁会话信息 */
- PGPROC *myProc; /* link to PGPROC of owning backend,指向进程PGPROC */
- } PROCLOCKTAG;
- typedef struct PROCLOCK
- {
- /* tag */
- PROCLOCKTAG tag; /* proclock 的唯一ID */
-
- /* data */
- PGPROC *groupLeader; /* proc's lock group leader, or proc itself,并行执行时,并行会话的leader */
- LOCKMASK holdMask; /* 当前会话在这个对象上持有的锁模式 */
- LOCKMASK releaseMask; /* 记录需要释放的锁模式 */
- SHM_QUEUE lockLink; /* list link in LOCK's list of proclocks,LOCK结构体的proclocks字段 */
- SHM_QUEUE procLink; /* list link in PGPROC's list of proclocks,PGPROC结构体的proclocks字段 */
- } PROCLOCK;
来看看初始化函数InitLocks,它的用途就是初始化我们反复提到的4个锁对象——主锁表、进程锁表、fast-path对象、本地锁表。
- /*
- * InitLocks -- Initialize the lock manager's data structures.
- */
- void
- InitLocks(void)
- {
- HASHCTL info;
- long init_table_size,
- max_table_size;
- bool found;
-
- /*
- * Compute init/max size to request for lock hashtables. Note these
- * calculations must agree with LockShmemSize!
- * 计算锁hash表的初始和最大大小,最大为max_table_size,初始大小为最大大小的一半
- */
- max_table_size = NLOCKENTS();
- init_table_size = max_table_size / 2;
初始化主锁表对象LOCK(LockMethodLockHash),主锁表用于保存每个锁对象信息
- /*
- * Allocate hash table for LOCK structs. This stores per-locked-object information.
- */
- info.keysize = sizeof(LOCKTAG);
- info.entrysize = sizeof(LOCK);
- info.num_partitions = NUM_LOCK_PARTITIONS;
-
- LockMethodLockHash = ShmemInitHash("LOCK hash",
- init_table_size,
- max_table_size,
- &info,
- HASH_ELEM | HASH_BLOBS | HASH_PARTITION);
-
- /* Assume an average of 2 holders per lock,假设平均每个锁有两个holders */
- max_table_size *= 2;
- init_table_size *= 2;
初始化进程锁表对象PROCLOCK(LockMethodProcLockHash),进程锁表用于保存每个锁对象的每个holder(per-lock-per-holder)信息
- /*
- * Allocate hash table for PROCLOCK structs. This stores
- * per-lock-per-holder information.
- */
- info.keysize = sizeof(PROCLOCKTAG);
- info.entrysize = sizeof(PROCLOCK);
- info.hash = proclock_hash;
- info.num_partitions = NUM_LOCK_PARTITIONS;
-
- LockMethodProcLockHash = ShmemInitHash("PROCLOCK hash",
- init_table_size,
- max_table_size,
- &info,
- HASH_ELEM | HASH_FUNCTION | HASH_PARTITION);
初始化fast-path变量 FastPathStrongRelationLocks(注意这不是个hash表,从它的初始化语句也能看出跟另外3个是不同的),记录是否有事务已获得这个对象的“强锁”。fast-path将对弱锁的访问保存到本进程,避免频繁访问主锁表和进程锁表。
- /*
- * Allocate fast-path structures.
- */
- FastPathStrongRelationLocks =
- ShmemInitStruct("Fast Path Strong Relation Lock Data",
- sizeof(FastPathStrongRelationLockData), &found);
- if (!found)
- SpinLockInit(&FastPathStrongRelationLocks->mutex);
初始化本地锁表对象LOCALLOCK(LockMethodLocalHash),本地锁表用于保存本地锁计数和资源属主信息,这不是个共享表。
- /*
- * Allocate non-shared hash table for LOCALLOCK structs. This stores lock
- * counts and resource owner information.
- */
- if (LockMethodLocalHash)
- hash_destroy(LockMethodLocalHash);
-
- info.keysize = sizeof(LOCALLOCKTAG);
- info.entrysize = sizeof(LOCALLOCK);
-
- LockMethodLocalHash = hash_create("LOCALLOCK hash",
- 16,
- &info,
- HASH_ELEM | HASH_BLOBS);
- }
实际上,本地锁表和fast path的主要目的都是提升性能,它们已经可以处理大多数情况,其他情况则需要对主锁表和进程锁表进行查询,根据锁的状态决定是获得锁还是进入等待。
SetupLockInTable函数的主要作用就是在主锁表和进程锁表中查找对应的锁,如果该锁还不存在,则在主锁表和进程锁表中申请内存来保存锁并初始化锁的信息。
会话1
会话2
函数调用栈如下:
- /*
- * Find or create LOCK and PROCLOCK objects as needed for a new lock
- * request.
- *
- * Returns the PROCLOCK object, or NULL if we failed to create the objects
- * for lack of shared memory.
- *
- * The appropriate partition lock must be held at entry, and will be
- * held at exit.
- */
- static PROCLOCK *
- SetupLockInTable(LockMethod lockMethodTable, PGPROC *proc,
- const LOCKTAG *locktag, uint32 hashcode, LOCKMODE lockmode)
- {
- LOCK *lock;
- PROCLOCK *proclock;
- PROCLOCKTAG proclocktag;
- uint32 proclock_hashcode;
- bool found;
-
- /*
- * Find or create a lock with this tag.从主锁表LockMethodLockHash中查找lock对象
- */
- lock = (LOCK *) hash_search_with_hash_value(LockMethodLockHash,
- (const void *) locktag,
- hashcode,
- HASH_ENTER_NULL,
- &found);
- if (!lock) //按照函数注释,return NULL表示由于共享内存不足报错了,lock为空(正常找不找得到都不应该是空)
- return NULL;
- /*
- * if it's a new lock object, initialize it,如果是新的锁,初始化锁对象,这个跟之前本地锁表类似
- */
- 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);
- }
- else //如果不是,输出已找到该锁,并做一些判断
- {
- LOCK_PRINT("LockAcquire: found", lock, lockmode);
- Assert((lock->nRequested >= 0) && (lock->requested[lockmode] >= 0));
- Assert((lock->nGranted >= 0) && (lock->granted[lockmode] >= 0));
- Assert(lock->nGranted <= lock->nRequested);
- }
这里因为found为false,需要初始化锁对象
- /*
- * Create the hash key for the proclock table. 为进程锁表创建hash key */
- proclocktag.myLock = lock;
- proclocktag.myProc = proc;
-
- proclock_hashcode = ProcLockHashCode(&proclocktag, hashcode);
-
- /*
- * Find or create a proclock entry with this tag,根据lock和PGPROC信息在进程锁表LockMethodProcLockHash中查找
- */
- proclock = (PROCLOCK *) hash_search_with_hash_value(LockMethodProcLockHash,
- (void *) &proclocktag,
- proclock_hashcode,
- HASH_ENTER_NULL,
- &found);
- if (!proclock) // 这里一样,如果proclock为空表示由于共享内存不足报错了,需要把主锁表中的信息也删除,防止内存泄漏
- {
- /* Oops, not enough shmem for the proclock */
- if (lock->nRequested == 0)
- {
- /*
- * There are no other requestors of this lock, so garbage-collect
- * the lock object. We *must* do this to avoid a permanent leak
- * of shared memory, because there won't be anything to cause
- * anyone to release the lock object later.
- */
- Assert(SHMQueueEmpty(&(lock->procLocks)));
- if (!hash_search_with_hash_value(LockMethodLockHash,
- (void *) &(lock->tag),
- hashcode,
- HASH_REMOVE,
- NULL))
- elog(PANIC, "lock table corrupted");
- }
- return NULL;
- }
-
- /*
- * If new, initialize the new entry,如果没找到,初始化进程锁表
- */
- if (!found)
- {
- uint32 partition = LockHashPartition(hashcode);
- proclock->groupLeader = proc->lockGroupLeader != NULL ?
- proc->lockGroupLeader : proc;
- proclock->holdMask = 0;
- proclock->releaseMask = 0;
- /* Add proclock to appropriate lists */
- SHMQueueInsertBefore(&lock->procLocks, &proclock->lockLink);
- SHMQueueInsertBefore(&(proc->myProcLocks[partition]),
- &proclock->procLink);
- PROCLOCK_PRINT("LockAcquire: new", proclock);
- }
这里因为没找到proclock,需要初始化
- else //否则,输出找到了,并做一些判断
- {
- PROCLOCK_PRINT("LockAcquire: found", proclock);
- Assert((proclock->holdMask & ~lock->grantMask) == 0);
-
- #ifdef CHECK_DEADLOCK_RISK
-
- //死锁风险判断
- {
- int i;
-
- for (i = lockMethodTable->numLockModes; i > 0; i--) //从拥有的最高级模式锁开始检查
- {
- if (proclock->holdMask & LOCKBIT_ON(i))
- {
- if (i >= (int) lockmode)//如果持锁等级>=请求等级,不会有死锁风险
- break; /* safe: we have a lock >= req level */
- elog(LOG, "deadlock risk: raising lock level"
- " from %s to %s on object %u/%u/%u",
- lockMethodTable->lockModeNames[i],
- lockMethodTable->lockModeNames[lockmode],
- lock->tag.locktag_field1, lock->tag.locktag_field2,
- lock->tag.locktag_field3);
- break;
- }
- }
- }
- #endif /* CHECK_DEADLOCK_RISK */
- }
- /*
- * lock->nRequested and lock->requested[] count the total number of
- * requests, whether granted or waiting, so increment those immediately.
- * The other counts don't increment till we get the lock.
- */
- lock->nRequested++; //总的锁请求模式加1
- lock->requested[lockmode]++; // 对应的锁模式请求数加1
- Assert((lock->nRequested > 0) && (lock->requested[lockmode] > 0));
-
- /*
- * We shouldn't already hold the desired lock; else locallock table is
- * broken.
- */
- if (proclock->holdMask & LOCKBIT_ON(lockmode))
- elog(ERROR, "lock %s on object %u/%u/%u is already held",
- lockMethodTable->lockModeNames[lockmode],
- lock->tag.locktag_field1, lock->tag.locktag_field2,
- lock->tag.locktag_field3);
-
- return proclock;
- }
因为found为false,直接跳到了最后,执行完成
参考
《PostgreSQL技术内幕:事务处理深度探索》第2章
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。