数据库并发事务控制四:postgresql数据库的锁机制

并发控制是DBMS的关键技术,并发控制技术也称为同步机制,其实现通常依赖于底层的并发控制机制。操作系统提供了多种同步对象,如事件 Event、互斥锁 Mutex和条件变量 Cond、信号量Semaphore、读写锁 RWLock、自旋锁 Spinlock等。数据库管理系统自己实现封锁主要是考虑:
    锁语义加强:OS只提供排它锁。为了提高并发度,数据库至少需要共享锁和排它锁,即读锁和写锁;
    锁的功能增强:数据库提供视图监测封锁情况和进行死锁检测;
    可靠性的考虑:例如,在某个持锁进程任意一点回滚时,数据库系统要能够释放其占有的锁;
    可移植性的考虑:各个平台的封锁并没有完全对等的一一匹配;
    效率的考虑;例如使用spinlock来提高在SMP处理机上的性能;
但是最终,数据库系统都是依靠操作系统和硬件平台提供的同步机制来实现自己的封锁。pg中提供了自旋锁、轻量锁以及常规锁。自旋锁和轻量锁都是pg内部使用的。常规锁pg内部使用,用户也可以通过select * for update等指定锁。
先把pg系统内核里锁相关的关键结构和变量吧。

自旋锁:
用于锁锁或其它内存结构,对用户透明,不支持死锁检测。
pg中实现自旋锁的几个相关文件是spin.h, spin.c, s_lock.h, s_lock.c。
  spin.h 定义了自旋锁的接口函数/宏。
  spin.c 定义了一个利用信号量实现的自旋锁,如果目标平台没有自己的自旋锁就用这个。
  s_lock.h 定义了硬件、操作系统平台相关的自旋锁的实现。
自旋锁在少数几个地方被直接使用,它主要是作为实现轻量锁的一个手段。每个LWLOCK都需要有一个自旋锁。

轻量锁:
轻量锁有读锁(LW_SHARED)和写锁(LW_EXCLUSIVE),对用户透明,不支持死锁检测,用于锁内存结构,保持临界区进程间互斥,共享内存中分配控制结构的空间。

主要接口函数在 lwlock.h 文件中定义。下面是域定义的轻量锁。
/*
 * We have a number of predefined LWLocks, plus a bunch of LWLocks that are
 * dynamically assigned (e.g., for shared buffers).  The LWLock structures
 * live in shared memory (since they contain shared data) and are identified
 * by values of this enumerated type.  We abuse the notion of an enum somewhat
 * by allowing values not listed in the enum declaration to be assigned.
 * The extra value MaxDynamicLWLock is there to keep the compiler from
 * deciding that the enum can be represented as char or short ...
 *
 * If you remove a lock, please replace it with a placeholder. This retains
 * the lock numbering, which is helpful for DTrace and other external
 * debugging scripts.
 */
typedef enum LWLockId
{
    BufFreelistLock,
    ShmemIndexLock,
    OidGenLock,
    XidGenLock,
    ProcArrayLock,
    SInvalReadLock,
    SInvalWriteLock,
    WALInsertLock,
    WALWriteLock,
    ControlFileLock,
    CheckpointLock,
    CLogControlLock,
    SubtransControlLock,
    MultiXactGenLock,
    MultiXactOffsetControlLock,
    MultiXactMemberControlLock,
    RelCacheInitLock,
    CheckpointerCommLock,
    TwoPhaseStateLock,
    TablespaceCreateLock,
    BtreeVacuumLock,
    AddinShmemInitLock,
    AutovacuumLock,
    AutovacuumScheduleLock,
    SyncScanLock,
    RelationMappingLock,
    AsyncCtlLock,
    AsyncQueueLock,
    SerializableXactHashLock,
    SerializableFinishedListLock,
    SerializablePredicateLockListLock,
    OldSerXidLock,
    SyncRepLock,
    /* Individual lock IDs end here */
    FirstBufMappingLock,
    FirstLockMgrLock = FirstBufMappingLock + NUM_BUFFER_PARTITIONS,
    FirstPredicateLockMgrLock = FirstLockMgrLock + NUM_LOCK_PARTITIONS,

    /* must be last except for MaxDynamicLWLock: */
    NumFixedLWLocks = FirstPredicateLockMgrLock + NUM_PREDICATELOCK_PARTITIONS,

    MaxDynamicLWLock = 1000000000
} LWLockId;

typedef enum LWLockMode
{
    LW_EXCLUSIVE,
    LW_SHARED,
    LW_WAIT_UNTIL_FREE            /* A special mode used in PGPROC->lwlockMode,
                                 * when waiting for lock to become free. Not
                                 * to be used as LWLockAcquire argument */
} LWLockMode;


pg系统中都有哪些轻量锁呢?可以从src/backend/storage/lmgr/lwlock.c文件中的方法CreateLWLocks()->NumLWLocks()查看,这个方法的作用是Allocate shmem space for LWLocks and initialize the locks。

int
NumLWLocks(void)
{
        int                     numLocks;

        /*
         * Possibly this logic should be spread out among the affected modules,
         * the same way that shmem space estimation is done.  But for now, there
         * are few enough users of LWLocks that we can get away with just keeping
         * the knowledge here.
         */

        /* Predefined LWLocks */
        numLocks = (int) NumFixedLWLocks;

        /* bufmgr.c needs two for each shared buffer */
        numLocks += 2 * NBuffers;

        /* proc.c needs one for each backend or auxiliary process */
        numLocks += MaxBackends + NUM_AUXILIARY_PROCS;

        /* clog.c needs one per CLOG buffer */
        numLocks += CLOGShmemBuffers();

        /* subtrans.c needs one per SubTrans buffer */
        numLocks += NUM_SUBTRANS_BUFFERS;

        /* multixact.c needs two SLRU areas */
        numLocks += NUM_MXACTOFFSET_BUFFERS + NUM_MXACTMEMBER_BUFFERS;

        /* async.c needs one per Async buffer */
        numLocks += NUM_ASYNC_BUFFERS;

        /* predicate.c needs one per old serializable xid buffer */
        numLocks += NUM_OLDSERXID_BUFFERS;

        /*
         * Add any requested by loadable modules; for backwards-compatibility
         * reasons, allocate at least NUM_USER_DEFINED_LWLOCKS of them even if
         * there are no explicit requests.
         */
        lock_addin_request_allowed = false;
        numLocks += Max(lock_addin_request, NUM_USER_DEFINED_LWLOCKS);

        return numLocks;
}


简单前后相关过程可以参见我的博文《PostgreSQL启动过程中的那些事七:初始化共享内存和信号一:初始化shmemIndex和信号 》
http://blog.csdn.net/beiigang/article/details/7163465

常规锁(Lock):
我们主要看常规锁。常规锁是数据库特有的锁机制,提供了丰富的锁模式,可以自动进行死锁检测和解死锁。实现要比前两种复杂许多。

看看和常规锁相关的几个结构和变量,具体参见src/include/storage/lock.h:
LOCKMETHOD:封锁方法,有两种,如下:
  /* These identify the known lock methods */
  #define DEFAULT_LOCKMETHOD      1
  #define USER_LOCKMETHOD         2

前者是系统内部加锁,这个加解锁过程对用户是透明的,例如用户在做SELECT时,系统就自动加AccessShareLock锁防止修改表结构等并发操作。后者是用户锁,即用户使用LOCK语句加的锁,这种锁需要用户显式地解锁或者用户出现事务故障自动解锁。

LOCKMODE:锁的模式,总共有8种。NoLock不算。
/*
 * These are the valid values of type LOCKMODE for all the standard lock
 * methods (both DEFAULT and USER).
 */

/* NoLock is not a lock mode, but a flag value meaning "don't get a lock" */
#define NoLock                          0

#define AccessShareLock                 1               /* SELECT */
#define RowShareLock                    2               /* SELECT FOR UPDATE/FOR SHARE */
#define RowExclusiveLock                3               /* INSERT, UPDATE, DELETE */
#define ShareUpdateExclusiveLock        4              /* VACUUM (non-FULL),ANALYZE, CREATE INDEX CONCURRENTLY */
#define ShareLock                       5               /* CREATE INDEX (WITHOUT CONCURRENTLY) */
#define ShareRowExclusiveLock           6               /* like EXCLUSIVE MODE, but allows ROW SHARE */
#define ExclusiveLock                   7               /* blocks ROW SHARE/SELECT...FOR UPDATE */
#define AccessExclusiveLock             8               /* ALTER TABLE, DROP TABLE, VACUUM FULL, and unqualified LOCK TABLE *
/

LockTagType: 被锁对象种类,也有用户锁和ADVISORY锁(这个在pg9.1新增加了trasaction类型的,原来只有session类型的)
/*
 * LOCKTAG is the key information needed to look up a LOCK item in the
 * lock hashtable.  A LOCKTAG value uniquely identifies a lockable object.
 *
 * The LockTagType enum defines the different kinds of objects we can lock.
 * We can handle up to 256 different LockTagTypes.
 */
typedef enum LockTagType
{
        LOCKTAG_RELATION,                       /* whole relation */
        /* ID info for a relation is DB OID + REL OID; DB OID = 0 if shared */
        LOCKTAG_RELATION_EXTEND,        /* the right to extend a relation */
        /* same ID info as RELATION */
        LOCKTAG_PAGE,                           /* one page of a relation */
        /* ID info for a page is RELATION info + BlockNumber */
        LOCKTAG_TUPLE,                          /* one physical tuple */
        /* ID info for a tuple is PAGE info + OffsetNumber */
        LOCKTAG_TRANSACTION,            /* transaction (for waiting for xact done) */
        /* ID info for a transaction is its TransactionId */
        LOCKTAG_VIRTUALTRANSACTION, /* virtual transaction (ditto) */
        /* ID info for a virtual transaction is its VirtualTransactionId */
        LOCKTAG_OBJECT,                         /* non-relation database object */
        /* ID info for an object is DB OID + CLASS OID + OBJECT OID + SUBID */

        /*
         * Note: object ID has same representation as in pg_depend and
         * pg_description, but notice that we are constraining SUBID to 16 bits.
         * Also, we use DB OID = 0 for shared objects such as tablespaces.
         */
        LOCKTAG_USERLOCK,                       /* reserved for old contrib/userlock code */
        LOCKTAG_ADVISORY                        /* advisory user locks */
} LockTagType;

#define LOCKTAG_LAST_TYPE       LOCKTAG_ADVISORY


LOCKTAG:被加锁的对象的标识:

/*

 * The LOCKTAG struct is defined with malice aforethought to fit into 16
 * bytes with no padding.  Note that this would need adjustment if we were
 * to widen Oid, BlockNumber, or TransactionId to more than 32 bits.
 *
 * We include lockmethodid in the locktag so that a single hash table in
 * shared memory can store locks of different lockmethods.
 */
typedef struct LOCKTAG
{
    uint32        locktag_field1; /* a 32-bit ID field:用于标识数据库dbid */
    uint32        locktag_field2; /* a 32-bit ID field:用于标识关系relid */
    uint32        locktag_field3; /* a 32-bit ID field:用于标识页blocknum */
    uint16        locktag_field4; /* a 16-bit ID field:用于标识行偏移offnum */
    uint8        locktag_type;    /* see enum LockTagType */
    uint8        locktag_lockmethodid;    /* lockmethod indicator */
} LOCKTAG
;

如LOCKTAG的注释里所说,常规锁在pg系统里是通过shared memory里的hash表:常规锁管理器——"LOCK hash"管理的,有兴趣的可以参考我的博文《PostgreSQL启动过程中的那些事七:初始化共享内存和信号八:shmem中初始化常规锁管理器 》
http://blog.csdn.net/beiigang/article/details/7299804

常规锁机制可以参考pg的官方手册,章节和内容见下面
13.3. Explicit Locking
http://www.postgresql.org/docs/9.4/static/explicit-locking.html
这节分为:表锁、行锁、页锁、死锁、Advisory锁(这个名字怎么翻译好??? 忠告锁,公告锁,咨询锁???)。

后面的内容提纲就是:
表锁、
行锁、
页锁、
死锁、
Advisory锁(这个名字怎么翻译好??? 忠告锁,公告锁,咨询锁???)、
查看锁。


感觉有些累了,懒了,不想写了,后面看情况到网上找些同仁同志同学朋友们的文章,放到这儿归类,如有需要待下次按我的思路再整理组织,今次先把题目做完整吧。


这篇先到这儿吧。





-----------------

转载请著明出处:
blog.csdn.net/beiigang

你可能感兴趣的:(数据库,锁,hash,Lock,并发控制,Advisory锁)