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

2015-02-02 13:19:49 · 作者: · 浏览: 42
并发控制是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。
简单前后相关过程可以参见我的博文《PostgreSQL启动过程中的那些事七:初始化共享内存和信号一:初始化shmemIndex和信号 》

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

看看和常规锁相关的几个结构和变量,
LOCKMETHOD:封锁方法,有两种,如下:
/* These identify the known lock methods */
#define DEF