本節(jié)是PostgreSQL Locks的概要部分,翻譯自README文件.
創(chuàng)新互聯(lián)科技有限公司專業(yè)互聯(lián)網(wǎng)基礎(chǔ)服務(wù)商,為您提供電信機(jī)房托管,高防物理服務(wù)器租用,成都IDC機(jī)房托管,成都主機(jī)托管等互聯(lián)網(wǎng)服務(wù)。
src/backend/storage/lmgr/README
Locking Overview
================
Postgres uses four types of interprocess locks:
PG使用四種類型的鎖:
* Spinlocks. These are intended for *very* short-term locks. If a lock
is to be held more than a few dozen instructions, or across any sort of
kernel call (or even a call to a nontrivial subroutine), don't use a
spinlock. Spinlocks are primarily used as infrastructure for lightweight
locks. They are implemented using a hardware atomic-test-and-set
instruction, if available. Waiting processes busy-loop until they can
get the lock. There is no provision for deadlock detection, automatic
release on error, or any other nicety. There is a timeout if the lock
cannot be gotten after a minute or so (which is approximately forever in
comparison to the intended lock hold time, so this is certainly an error
condition).
* Spinlocks(自旋鎖).這種一種非常短期的鎖.如果持有鎖會超過幾十個(gè)指令周期,或者
跨越多個(gè)類型的內(nèi)核調(diào)用,那么不要使用自旋鎖.
如可用的話(部分平臺并不可用),Spinlocks主要用于輕量級鎖的基礎(chǔ)"設(shè)施".
等待進(jìn)程會一直等待直至獲取到鎖(等待1分鐘則超時(shí)).沒有提供死鎖檢測/錯誤時(shí)自動釋放.
* Lightweight locks (LWLocks). These locks are typically used to
interlock access to datastructures in shared memory. LWLocks support
both exclusive and shared lock modes (for read/write and read-only
access to a shared object). There is no provision for deadlock
detection, but the LWLock manager will automatically release held
LWLocks during elog() recovery, so it is safe to raise an error while
holding LWLocks. Obtaining or releasing an LWLock is quite fast (a few
dozen instructions) when there is no contention for the lock. When a
process has to wait for an LWLock, it blocks on a SysV semaphore so as
to not consume CPU time. Waiting processes will be granted the lock in
arrival order. There is no timeout.
* Lightweight locks (輕量級鎖,LWLocks).這些鎖典型的用于保護(hù)共享內(nèi)存中的數(shù)據(jù)結(jié)構(gòu).
LWLocks提供了共享和獨(dú)占模式,沒有提供死鎖檢測,但在elog()恢復(fù)期間自動釋放持有的鎖,
因此在持有輕量級鎖時(shí)拋出異常是安全的.
沒有沖突的情況下,獲取或者釋放輕量級鎖是相當(dāng)快的(幾十個(gè)指令周期).
如進(jìn)程從必須等到LWLock,則會阻塞等待SysV信號量,這樣可以不需要耗費(fèi)CPU時(shí)間.
等待進(jìn)程按到達(dá)順序授予鎖.LWLocks沒有超時(shí)機(jī)制.
* Regular locks (a/k/a heavyweight locks). The regular lock manager
supports a variety of lock modes with table-driven semantics, and it has
full deadlock detection and automatic release at transaction end.
Regular locks should be used for all user-driven lock requests.
* Regular locks (重量級鎖,a/k/a heavyweight locks).
常規(guī)的鎖管理支持多種表驅(qū)動語義上的鎖模式,有完善的死鎖檢測機(jī)制并在事務(wù)結(jié)束時(shí)自動釋放鎖.
常規(guī)鎖應(yīng)在所有用戶驅(qū)動的鎖請求中使用.
* SIReadLock predicate locks. See separate README-SSI file for details.
* SIReadLock predicate locks(SIReadLock謂詞鎖).詳情參見README-SSI.
Acquisition of either a spinlock or a lightweight lock causes query
cancel and die() interrupts to be held off until all such locks are
released. No such restriction exists for regular locks, however. Also
note that we can accept query cancel and die() interrupts while waiting
for a regular lock, but we will not accept them while waiting for
spinlocks or LW locks. It is therefore not a good idea to use LW locks
when the wait time might exceed a few seconds.
自旋鎖/輕量級鎖都會導(dǎo)致取消查詢和die()中斷被延遲,直至釋放鎖.
對于常規(guī)鎖就沒有這樣的限制.
同時(shí),要注意在等待常規(guī)鎖時(shí)可以接受取消查詢和die()中斷,但在等待自旋鎖/輕量級鎖時(shí)則不接受.
如果等待時(shí)間超過幾十秒,使用LWLocks并不是明智的選擇.
The rest of this README file discusses the regular lock manager in detail.
Lock Data Structures
--------------------
Lock數(shù)據(jù)結(jié)構(gòu)
Lock methods describe the overall locking behavior. Currently there are
two lock methods: DEFAULT and USER.
鎖定方法描述了鎖定動作的概覽.目前有兩種鎖定方法:DEFAULT和USER.
Lock modes describe the type of the lock (read/write or shared/exclusive).
In principle, each lock method can have its own set of lock modes with
different conflict rules, but currently DEFAULT and USER methods use
identical lock mode sets. See src/include/storage/lock.h for more details.
(Lock modes are also called lock types in some places in the code and
documentation.)
鎖定模式描述了lock(R/W或共享/獨(dú)占)的類型.
原則上,每一種鎖方法可以有自己的鎖模式集合和不同的沖突原則,但當(dāng)前的DEFAULT/USER方法
會使用相同的鎖模式集合,詳情參見src/include/storage/lock.h
(鎖模式在代碼和文檔中的某些地方也被稱為鎖類型)
There are two main methods for recording locks in shared memory. The primary
mechanism uses two main structures: the per-lockable-object LOCK struct, and
the per-lock-and-requestor PROCLOCK struct. A LOCK object exists for each
lockable object that currently has locks held or requested on it. A PROCLOCK
struct exists for each backend that is holding or requesting lock(s) on each
LOCK object.
在共享內(nèi)存中保存鎖信息有兩種主要的方法.其中一種主要的機(jī)制使用兩種結(jié)構(gòu)體:
LOCK:每一個(gè)鎖定對象一個(gè),per-lockable-object.存儲每一個(gè)鎖定的對象(持有鎖或者等待鎖).
PROCLOCK:每一個(gè)鎖和請求者一個(gè),per-lock-and-requestor.存儲每一個(gè)持有/請求LOCK對象的后端進(jìn)程.
There is also a special "fast path" mechanism which backends may use to
record a limited number of locks with very specific characteristics: they must
use the DEFAULT lockmethod; they must represent a lock on a database relation
(not a shared relation), they must be a "weak" lock which is unlikely to
conflict (AccessShareLock, RowShareLock, or RowExclusiveLock); and the system
must be able to quickly verify that no conflicting locks could possibly be
present. See "Fast Path Locking", below, for more details.
另外,還有一種成為"fast path"的機(jī)制,后臺進(jìn)程可使用非常規(guī)特性的用于記錄有限數(shù)量的鎖,
這種情況下必須使用DEFAULT鎖方法.它們必須表示數(shù)據(jù)庫關(guān)系(非共享關(guān)系)上的鎖,
它們必須是一個(gè)不太可能出現(xiàn)沖突的"弱"鎖(AccessShareLock, RowShareLock, or RowExclusiveLock);
系統(tǒng)必須能夠快速的驗(yàn)證沖突鎖有沒有可能出現(xiàn).詳細(xì)參見下面的"Fast Path Locking".
Each backend also maintains an unshared LOCALLOCK structure for each lockable
object and lock mode that it is currently holding or requesting. The shared
lock structures only allow a single lock grant to be made per lockable
object/lock mode/backend. Internally to a backend, however, the same lock may
be requested and perhaps released multiple times in a transaction, and it can
also be held both transactionally and session-wide. The internal request
counts are held in LOCALLOCK so that the shared data structures need not be
accessed to alter them.
每一個(gè)后臺進(jìn)程同時(shí)會維護(hù)非共享的LOCALLOCK結(jié)構(gòu)體,該結(jié)構(gòu)體存儲鎖定對象和當(dāng)前持有/請求的鎖模式.
共享鎖結(jié)構(gòu)體只允許為每個(gè)鎖定對象/鎖模式/后臺進(jìn)程授予一個(gè)鎖.
但是,在后臺進(jìn)程內(nèi)部,同一個(gè)鎖可能在同一個(gè)事務(wù)中請求和釋放多次,并且可以事務(wù)/會話模式持有.
內(nèi)部請求的計(jì)數(shù)在LOCALLOCK結(jié)構(gòu)體中存儲以便共享數(shù)據(jù)結(jié)構(gòu)體在更新時(shí)不需要訪問.
LOCK
/*
* Per-locked-object lock information:
*
* tag -- uniquely identifies the object being locked
* grantMask -- bitmask for all lock types currently granted on this object.
* waitMask -- bitmask for all lock types currently awaited on this object.
* procLocks -- list of PROCLOCK objects for this lock.
* waitProcs -- queue of processes waiting for this lock.
* requested -- count of each lock type currently requested on the lock
* (includes requests already granted!!).
* nRequested -- total requested locks of all types.
* granted -- count of each lock type currently granted on the lock.
* nGranted -- total granted locks of all types.
*
* Note: these counts count 1 for each backend. Internally to a backend,
* there may be multiple grabs on a particular lock, but this is not reflected
* into shared memory.
*/
typedef struct LOCK
{
/* hash key */
LOCKTAG tag; /* unique identifier of lockable object */
/* data */
LOCKMASK grantMask; /* bitmask for lock types already granted */
LOCKMASK waitMask; /* bitmask for lock types awaited */
SHM_QUEUE procLocks; /* list of PROCLOCK objects assoc. with lock */
PROC_QUEUE waitProcs; /* list of PGPROC objects waiting on lock */
int requested[MAX_LOCKMODES]; /* counts of requested locks */
int nRequested; /* total of requested[] array */
int granted[MAX_LOCKMODES]; /* counts of granted locks */
int nGranted; /* total of granted[] array */
} LOCK;
#define LOCK_LOCKMETHOD(lock) ((LOCKMETHODID) (lock).tag.locktag_lockmethodid)
PROCLOCK
/*
* We may have several different backends holding or awaiting locks
* on the same lockable object. We need to store some per-holder/waiter
* information for each such holder (or would-be holder). This is kept in
* a PROCLOCK struct.
*
* PROCLOCKTAG is the key information needed to look up a PROCLOCK item in the
* proclock hashtable. A PROCLOCKTAG value uniquely identifies the combination
* of a lockable object and a holder/waiter for that object. (We can use
* pointers here because the PROCLOCKTAG need only be unique for the lifespan
* of the PROCLOCK, and it will never outlive the lock or the proc.)
*
* Internally to a backend, it is possible for the same lock to be held
* for different purposes: the backend tracks transaction locks separately
* from session locks. However, this is not reflected in the shared-memory
* state: we only track which backend(s) hold the lock. This is OK since a
* backend can never block itself.
*
* The holdMask field shows the already-granted locks represented by this
* proclock. Note that there will be a proclock object, possibly with
* zero holdMask, for any lock that the process is currently waiting on.
* Otherwise, proclock objects whose holdMasks are zero are recycled
* as soon as convenient.
*
* releaseMask is workspace for LockReleaseAll(): it shows the locks due
* to be released during the current call. This must only be examined or
* set by the backend owning the PROCLOCK.
*
* Each PROCLOCK object is linked into lists for both the associated LOCK
* object and the owning PGPROC object. Note that the PROCLOCK is entered
* into these lists as soon as it is created, even if no lock has yet been
* granted. A PGPROC that is waiting for a lock to be granted will also be
* linked into the lock's waitProcs queue.
*/
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 */
} PROCLOCKTAG;
typedef struct PROCLOCK
{
/* tag */
PROCLOCKTAG tag; /* unique identifier of proclock object */
/* data */
PGPROC *groupLeader; /* proc's lock group leader, or proc itself */
LOCKMASK holdMask; /* bitmask for lock types currently held */
LOCKMASK releaseMask; /* bitmask for lock types to be released */
SHM_QUEUE lockLink; /* list link in LOCK's list of proclocks */
SHM_QUEUE procLink; /* list link in PGPROC's list of proclocks */
} PROCLOCK;
#define PROCLOCK_LOCKMETHOD(proclock) \
LOCK_LOCKMETHOD(*((proclock).tag.myLock))
LOCALLOCK
/*
* Each backend also maintains a local hash table with information about each
* lock it is currently interested in. In particular the local table counts
* the number of times that lock has been acquired. This allows multiple
* requests for the same lock to be executed without additional accesses to
* shared memory. We also track the number of lock acquisitions per
* ResourceOwner, so that we can release just those locks belonging to a
* particular ResourceOwner.
*
* When holding a lock taken "normally", the lock and proclock fields always
* point to the associated objects in shared memory. However, if we acquired
* the lock via the fast-path mechanism, the lock and proclock fields are set
* to NULL, since there probably aren't any such objects in shared memory.
* (If the lock later gets promoted to normal representation, we may eventually
* update our locallock's lock/proclock fields after finding the shared
* objects.)
*
* Caution: a locallock object can be left over from a failed lock acquisition
* attempt. In this case its lock/proclock fields are untrustworthy, since
* the shared lock object is neither held nor awaited, and hence is available
* to be reclaimed. If nLocks > 0 then these pointers must either be valid or
* NULL, but when nLocks == 0 they should be considered garbage.
*/
typedef struct LOCALLOCKTAG
{
LOCKTAG lock; /* identifies the lockable object */
LOCKMODE mode; /* lock mode for this table entry */
} LOCALLOCKTAG;
typedef struct LOCALLOCKOWNER
{
/*
* Note: if owner is NULL then the lock is held on behalf of the session;
* otherwise it is held on behalf of my current transaction.
*
* Must use a forward struct reference to avoid circularity.
*/
struct ResourceOwnerData *owner;
int64 nLocks; /* # of times held by this owner */
} LOCALLOCKOWNER;
typedef struct LOCALLOCK
{
/* tag */
LOCALLOCKTAG tag; /* unique identifier of locallock entry */
/* data */
uint32 hashcode; /* copy of LOCKTAG's hash value */
LOCK *lock; /* associated LOCK object, if any */
PROCLOCK *proclock; /* associated PROCLOCK object, if any */
int64 nLocks; /* total number of times lock is held */
int numLockOwners; /* # of relevant ResourceOwners */
int maxLockOwners; /* allocated size of array */
LOCALLOCKOWNER *lockOwners; /* dynamically resizable array */
bool holdsStrongLockCount; /* bumped FastPathStrongRelationLocks */
bool lockCleared; /* we read all sinval msgs for lock */
} LOCALLOCK;
#define LOCALLOCK_LOCKMETHOD(llock) ((llock).tag.lock.locktag_lockmethodid)
README