[PGSQL]PostgreSQL表锁解析

在PostgreSQL中有8种表锁,锁之间的冲突关系如下表格:

enter image description here

初看上表,可能会感觉上表中的冲突关系很难记。下面就详细解析这些锁类型,通过解析之后,大家就容易理解了。

从刚开始,表级锁只有“SHARE”和“EXCLUSIVE”两种锁,这两种锁也很容易理解,基本就是读写锁的意思。加了“SHARE”锁后相当于加了读锁,表的内容就不能变化了,但多个事务都可以加上这个锁,只要任意一个人不释放这个读锁,则其它人都不能修改这个表了。加上了“EXCLUSIVE”后,相当于加了写锁,这时别的进程既不能写也不能读这条数据。但是,后来数据库加上多版本的功能。多版本的结果就是,如果我改了一行数据,实际上并没有改原先那行数据,而是先复制出一个新行,然后在新行上改,事务不提交,别人是看不到这条数据了。由于旧的一行的数据没有变化,在修改过程中,读数据的人仍然可以读到旧的数据,这样就没有必要让别人不能读数据了。这样在多版本下,除了以前的“SHARE”锁和“EXCLUSION”锁外,还需要增加两个锁,一个的锁叫“ACCESS SHARE”,表明加上这个锁,即使正在的修改数据的情况下,还是让读数据的,另一个锁叫作“ACCESS EXCLUSION”,就是即使有多版本的功能,也不允许访问数据。以前的“SHARE”锁,还叫“SHARE”锁,意思还是与原先一样,加了这个锁下,就不能对表作任何的变更;同样以前的“EXCLUSIVE”锁还叫“EXCLUSIVE”锁,意思与原来的意思也差不多,就是可以允许多版本的读锁之外,其它的读或写操作都被禁止。

表级锁的加锁对象是表,加锁的范围太大,导致并发性能不高,于是人们提出的行级锁的概念,但行级锁与与表级锁之间会产生冲突,这时就需要有一种机制来描述行级锁与表级锁之间的关系。在MySQL中是使用“意向锁”的概念来解决这个问题的,方法就是当我们要修改表中的一行数据时,需要先在表上加一种锁,表示我们即将在表的部分行上加共享锁或排它锁。PostgreSQL也是这样实现的,如“ROW SHARE”、“ROW EXCLUSIVE”这两个锁,这两个锁实际就对应MySQL中的意向共享锁(IS)和意向排它锁(IX)。从“意向锁”的概念出发,我们可以得到意向锁的下面两个特点:

  • 意向锁之间总是不会发生冲突的,即使“ROW EXCLUSIVE”之间也不会发生冲突的,因为它们都只是“有意”要做什么,还没有真做,所以是可以兼容的。
  • 意向锁与其它非意向锁之间的关系与普通锁与普通锁之间的关系是相同的。举例:“X”与“X”锁是冲突的,所以“IX”锁与“X”是冲突的;“X”与“S”锁是冲突的,所以“IX”锁与“S”也是冲突的;“S”与“S”锁是不冲突的,所以“IS”与“S”也是不冲突的。

如果我们把共享锁“SHARE”简写为“S”,排它锁“EXCLUSIVE”简写为“X”,把“ROW SHARE”简写为“IS”,把“ROW EXCLUSIVE”简写为“IX”,这四种锁之间的关系如下表所示:

X S IX IS
X N N N N
S N Y N Y
IX N N Y Y
IS N Y Y Y

我们知道意向排它锁“IX”与自己之间是不会冲突的,这时我们可能就需要一种稍严格的锁,就是自己与自己不兼容的锁,和其它锁的冲突情况下与“IX”相同,这种锁在PostgreSQL中就叫“SHARE UPDATE EXCLUSIVE”。不带 FULL选项的VACUUM、CREATE INDEX CONCURRENTLY命令都请求这样的锁。这也很好理解,这些命令除了不允许在同一个表上并发执行外,其它情况与更新表时对表加锁的需求是一样的。

在PostgreSQL中还有一种锁,称之为“SHARE ROW EXCLUSIVE”,这种锁我们可以看成是同时加了 “S”锁和“IX”锁的结果, PostgreSQL命令都不会自动请求这个锁模式,也就是PostgreSQL内部目前不会使用这种锁。

总结一下,PostgreSQL中有8种表锁,最普通的共享锁“SHARE”和排它锁“EXCLUSIVE”,因为多版本,修改一条数据的同时,允许了读数据,所以为了处理这种情况下,又加了两种锁“ACCESS SHARE”和“ACESS EXCLUSIVE”,所以锁中的“ACCESS”这个关键字是与多版本读相关的。由于为了处理表锁和行锁之间的关系,所以有了意向锁的概念,这时又加了两种锁,即意向共享锁和意向排它锁。这样总共就有了6种锁。由于意向锁之间不会产生冲突,即在意向排它锁与意向排它锁之间也不会产生冲突,而我们又需要更严格一些的锁,这样就产生了“SHARE UPDATE EXCLUSIVE”和“SHARE ROW EXCLUSIVE”两种锁,这样总共8种锁。