The trade-off between postgresql regular lock application and release and fastpath fast application optimization

​Column content :
postgresql kernel source code analysis
handwritten database toadb
concurrent programming
personal homepage : my homepage
motto: Tian Xingjian, a gentleman strives for self-improvement;

==================================

definition

Each conventional lock needs to define several elements, which are defined by the structure LockMethodData;

typedef struct LockMethodData
{
    
    
	int			numLockModes;
	const LOCKMASK *conflictTab;
	const char *const *lockModeNames;
	const bool *trace_flag;
} LockMethodData;

typedef const LockMethodData *LockMethod;

These elements are:

  • The mode type of the lock, that is, the lock is divided into several locking methods. For example, there are 8 types of table locks here, that is, 8-level table locks;
  • Lock conflict matrix, which is a two-dimensional table by bit, that is, the conflict relationship between lock modes at all levels, such as read-write mutual exclusion, read-read non-conflict, etc.;
  • The name of the lock, mainly for finding and debugging;

Postgresql has defined a default lock default_lockmethod, and user locks can also be customized

storage

The regular lock is shared between multiple processes, so it is stored in shared memory.
Storage is organized by these structures:

  • LockMethodLockHash , use locktag as hash to store the lock used
  • LockMethodProcLockHash, the reference relationship of the storage lock is stored by the lock-proc pair, and the proc is the information of each backend
  • FastPathStrongRelationLocks ,
  • LockMethodLocalHash, store all the locks held by this process, if the lock is the same, only the reference count is incremented

The above hash table has been allocated during initialization

Apply

The application of conventional locks is mainly implemented in the interfaces LockAcquire and LockAcquireExtended.

LockAcquireResult
LockAcquire(const LOCKTAG *locktag,
			LOCKMODE lockmode,
			bool sessionLock,
			bool dontWait)
{
    
    
	return LockAcquireExtended(locktag, lockmode, sessionLock, dontWait,
							   true, NULL);
}

LockAcquireResult
LockAcquireExtended(const LOCKTAG *locktag,
					LOCKMODE lockmode,
					bool sessionLock,
					bool dontWait,
					bool reportMemoryError,
					LOCALLOCK **locallockp);

It can be seen that it is finally implemented in LockAcquireExtended, the former is just a simple call relationship;

application process

Let's look at the implementation process in LockAcquireExtended

  • Search from the local lock record; if found, return; if not found, create a new local record;
  • Special processing in standby mode; only read-only locks can be obtained, and at this time, the transaction ID needs to be obtained first, so that the lock can be locked with the transaction;
  • fastpath processing;

fastpath is only used in the table lock mode; it is enabled when the acquired lock is less than Level 4 ShareUpdateExclusiveLock;
fastpath can record FP_LOCK_SLOTS_PER_BACKEND 16 lock records, and take the modulo according to the locktag hash value. If the corresponding position is not occupied and count=0, fastpath will be performed ;
Acquire the lock through FastPathGrantRelationLock; when the corresponding bit is 0, the lock will be obtained directly, and the bit will be set to 1, and the corresponding reloid will be recorded in the reloid array; Obtained directly;
here are two variables (proc)->fpLockBits and (proc)->fpRelId[FP_LOCK_SLOTS_PER_BACKEND], the former records the lock mode, and the latter records the corresponding reloid; the former is a 64-bit integer, and each 4bit is a group , can record 16 locks;

  • Create or find a lock, and create a lock proclock

When applying for a table lock and the lock level is greater than 4, first check the conflict with the fastpath lock; first in FastPathStrongRelationLocks->count[fasthashcode] corresponds to +1, occupying the place; then use ProcGlobal to traverse the fastpath information in all processes; if If it is found that a backend has already occupied a lock below level 4 through fastpath, then create a lock and lockproc and add it to the corresponding hash, so that it will be found when the lock conflict is judged later;

  • Check for lock conflicts

Find the locktag of the lock from LockMethodLockHash, if there is already a holder; then create the holder relationship from LockMethodProcLockHash;

  • First check the conflict with the lock waiter, and check the conflict according to lock->waitMask; if there is a conflict, wait in the lock queue;
  • Then check the conflict with the lock already held to avoid deadlock waiting; this step is more complicated, first check the lock held, and then check the lock group conflict; after the
    above two steps, if there is no conflict, the lock will be obtained; if there is a conflict, the lock will be obtained wait in line;
  • Lock conflict waits until someone wakes up

freed

Release the lock after use. If there is a waiter who needs to wake up, handle it in LockRelease.

bool 
LockRelease(const LOCKTAG *locktag, LOCKMODE lockmode, bool sessionLock);

The main process is as follows:

  • Check whether the local record lock is held by LockMethodLocalHash;

Local holding, then first count the lock holding -1, if it is not zero, then release and return true;

If the lock count is zero, first set the number of locally held locks -1, and cancel the holding record from the resource management;

  • Cases for processing fastpath applications

If you hold a lock applied for fastpath, clear it from the fastpath information; and clear the local lock record, return true;

  • Of course the lock is fully released, removed from LockMethodLockHash and LockMethodProcLockHash
  • Check whether the lock is currently held, if not, clear the local lock record and return false;
  • Release the lock;
    lock grant and requested count are -1 respectively; if the current lock mode grant is 0, set the lock mode position of grantMask to 0; check
    whether there is a waiter, that is, see if there is a conflict in waitMask, and if so, it needs to be woken up;
    Set the current lock mode to 0 in the hold lock holdMask in proclock;
  • Clean up the lock and wake up the waiter;
    if the lock is no longer held, delete the lockproc from the hash table;
    if the requester of the current lock is 0, delete the lock from the hash table;
    if there is a requester, it is the lock Waiters need to wake up;

Traverse all waiters and check whether they can be woken up. When waking up, first grant the lock and then wake up to avoid re-competition;
the conditions for a waiter to be woken up are:

  1. There will be no lock conflict between the lock mode applied by the waiter and the previous waiter (not awakened);
  2. There will be no lock conflict with those who already hold the lock;
    if there is a conflict, they will not wake up;
  • Clear the local lock record and return true;

end

Thank you very much for your support. Don’t forget to leave your valuable comments while browsing. If you think it is worthy of encouragement, please like and bookmark, I will work harder!

Author email: [email protected]
If there are any mistakes or omissions, please point them out and learn from each other.

Note: Do not reprint without consent!

Supongo que te gusta

Origin blog.csdn.net/senllang/article/details/131747255
Recomendado
Clasificación