数据库并发事务控制四: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

时间: 2024-07-29 06:48:42

数据库并发事务控制四:postgresql数据库的锁机制的相关文章

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

在博文<数据库并发事务控制四:postgresql数据库的锁机制 > http://blog.csdn.net/beiigang/article/details/43302947 中后面提到: 常规锁机制可以参考pg的官方手册,章节和内容见下面 13.3. Explicit Locking http://www.postgresql.org/docs/9.4/static/explicit-locking.html 这节分为:表锁.行锁.页锁.死锁.Advisory锁(这个名字怎么翻译好???

数据库并发事务控制 一:综述

并发控制是DBMS的关键技术 对数据库的操作都是在事务中进行的. 事务是指一组相互依赖的操作行为.事务中的操作是不可分割的工作单元,由一组在业务逻辑上相互依赖的SQL语句组成,有ACID特征. Atomic(原子性):事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败. Consistency(一致性):只有合法的数据可以被写入数据库,否则事务应该将其回滚到最初状态. Isolation(隔离性):事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完

并发事务的丢失更新及数据锁机制

在事务的隔离级别内容中,能够了解到两个不同的事务在并发的时候可能会发生数据的影响.细心的话可以发现事务隔离级别章节中,脏读.不可重复读.幻读三个问题都是由事务A对数据进行修改.增加,事务B总是在做读操作.如果两事务都在对数据进行修改则会导致另外的问题:丢失更新.这是本博文所要叙述的主题,同时引出并发事务对数据修改的解决方案:锁机制. 1.丢失更新的定义及产生原因. 丢失更新就是两个不同的事务(或者Java程序线程)在某一时刻对同一数据进行读取后,先后进行修改.导致第一次操作数据丢失.可以用图表表

数据库 之 事务控制和隔离级别

1  概述 事务是指一组原子性的SQL查询.或者是一个或多个SQL语句组成的独立工作单元:MyISAM不流行的原因很大是因为其不支持事务的处理功能. 2  事务日志 事务日志定义属性,有些参数可以运行时修改,写入在配置段里,事务日志相当于是中间的辅助功能,而且很关键. 事务日志表示把操作过程一笔一笔记录下来.如某个线程要对某个行操作,数据库会先保留老版本于事务日志中,对文件的写入,新版本的内容是先写入到日志里,提交前,数据在日志文件中,而不是在数据文件中,提交操作执行后,才将新旧版本的日志清除.

python3.4多线程实现同步的四种方式(锁机制、条件变量、信号量和同步队列)

临界资源即那些一次只能被一个线程访问的资源,典型例子就是打印机,它一次只能被一个程序用来执行打印功能,因为不能多个线程同时操作,而访问这部分资源的代码通常称之为临界区. threading的Lock类,用该类的acquire函数进行加锁,用realease函数进行解锁 import threading import time class Num: def __init__(self): self.num = 0 self.lock = threading.Lock() def add(self)

数据库并发事务存在的三个问题(脏读、不可重复读、幻读)

如果不考虑事务隔离性,可能会发生以下情况 脏读:事务a,读到了事务b未提交的数据,如果事务a读到了事务b的一些中间数据,待处理的数据.b事务数据还没有提交,就被a事务访问了 (解决方法:将 读未提交 级别提高到 读已提交 例如:orale在事务a 更新t表的时候,表t为锁住的状态,事务a未提交之前,事务b就不能访问t表) 不可重复读:指的是事务两次读取数据不一样.因为中间被其他事务修改了.解决方法就是 将 级别提高到 可重复.使用行级锁,锁定当前记录,其它事务无法更改.针对update操作 幻读

Java并发编程系列(三)-locks锁机制类

Java中的锁可以分为"同步锁"和JUC包里面的锁(locks包) 同步锁:即通过synchronized关键字来进行同步,实现对竞争资源的互斥访问的锁.Java 1.0版本中就已经支持同步锁了.同步锁的原理是,对于每一个对象,有且仅有一个同步锁:不同的线程能共同访问该同步锁.但是,在同一个时间点,该同步锁能且只能被一个线程获取到.这样,获取到同步锁的线程就能进行CPU调度,从而在CPU上执行:而没有获取到同步锁的线程,必须进行等待,直到获取到同步锁之后才能继续运行. locks包:相

并发编程(四):也谈谈数据库的锁机制

http://www.2cto.com/database/201403/286730.html 1. 数据库并发的问题 数据库带来的并发问题包括: 1. 丢失更新. 2. 未确认的相关性(脏读). 3. 不一致的分析(非重复读). 4. 幻像读. 详细描述如下: 1.1.丢失更新 当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,会发生丢失更新问题.每个事务都不知道其它事务的存在.最后的更新将重写由其它事务所做的更新,这将导致数据丢失. e.g.事务A和事务B同时修改某行的值, 事务A

网上资料笔记总结!!数据库事务并发问题,锁机制和对应的4种隔离级别

数据库事务并发问题 数据库的操作通常为写和读,就是所说的CRUD:增加(Create).读取(Read).更新(Update)和删除(Delete).事务就是一件完整要做的事情.事务是恢复和并发控制的基本单位.事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少.事务在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序.是数据库中各种数据项的一个程序执行单元.事务是用户定义的一个操作序列(多个表同时读写).这些操作要么都做,要么都不做,是一个不可分割的工作单位