温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

PostgreSQL 源码解读(219)- Locks(Overview)

发布时间:2020-08-11 15:47:04 来源:ITPUB博客 阅读:239 作者:husthxd 栏目:关系型数据库

本节是PostgreSQL Locks的概要部分,翻译自README文件.

一、Overview

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(自旋锁).这种一种非常短期的锁.如果持有锁会超过几十个指令周期,或者
  跨越多个类型的内核调用,那么不要使用自旋锁.
  如可用的话(部分平台并不可用),Spinlocks主要用于轻量级锁的基础"设施".
  等待进程会一直等待直至获取到锁(等待1分钟则超时).没有提供死锁检测/错误时自动释放.
* 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).这些锁典型的用于保护共享内存中的数据结构.
  LWLocks提供了共享和独占模式,没有提供死锁检测,但在elog()恢复期间自动释放持有的锁,
  因此在持有轻量级锁时抛出异常是安全的.
  没有冲突的情况下,获取或者释放轻量级锁是相当快的(几十个指令周期).
  如进程从必须等到LWLock,则会阻塞等待SysV信号量,这样可以不需要耗费CPU时间.
  等待进程按到达顺序授予锁.LWLocks没有超时机制.
* 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). 
  常规的锁管理支持多种表驱动语义上的锁模式,有完善的死锁检测机制并在事务结束时自动释放锁.
  常规锁应在所有用户驱动的锁请求中使用.
* 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.
自旋锁/轻量级锁都会导致取消查询和die()中断被延迟,直至释放锁.
对于常规锁就没有这样的限制.
同时,要注意在等待常规锁时可以接受取消查询和die()中断,但在等待自旋锁/轻量级锁时则不接受.
如果等待时间超过几十秒,使用LWLocks并不是明智的选择.
The rest of this README file discusses the regular lock manager in detail.
Lock Data Structures
--------------------
Lock数据结构
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或共享/独占)的类型.
原则上,每一种锁方法可以有自己的锁模式集合和不同的冲突原则,但当前的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.
在共享内存中保存锁信息有两种主要的方法.其中一种主要的机制使用两种结构体:
LOCK:每一个锁定对象一个,per-lockable-object.存储每一个锁定的对象(持有锁或者等待锁).
PROCLOCK:每一个锁和请求者一个,per-lock-and-requestor.存储每一个持有/请求LOCK对象的后端进程.
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"的机制,后台进程可使用非常规特性的用于记录有限数量的锁,
这种情况下必须使用DEFAULT锁方法.它们必须表示数据库关系(非共享关系)上的锁,
它们必须是一个不太可能出现冲突的"弱"锁(AccessShareLock, RowShareLock, or RowExclusiveLock);
系统必须能够快速的验证冲突锁有没有可能出现.详细参见下面的"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.
每一个后台进程同时会维护非共享的LOCALLOCK结构体,该结构体存储锁定对象和当前持有/请求的锁模式.
共享锁结构体只允许为每个锁定对象/锁模式/后台进程授予一个锁.
但是,在后台进程内部,同一个锁可能在同一个事务中请求和释放多次,并且可以事务/会话模式持有.
内部请求的计数在LOCALLOCK结构体中存储以便共享数据结构体在更新时不需要访问.

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

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI