PG数据库中由两类锁:
- 表级锁
- 行级锁
当要查询、插入、更新、删除表中的数据时,首先会获得表上的锁,然后再获得行上的锁。
表级锁模式
锁模式 | 解释 | SQL操作 |
ACCESS SHARE | 只与ACCESS EXCLUSIVE模式冲突。 通常情况下,任何只读取表而不修改表的查询都会请求这种锁模式 用于保护事务正在读取的表不被其他事务修改或删除 | SELECT:当事务执行SELECT语句读取表中的数据时,会自动获取ACCESS SHARE锁模式 COPY:当事务执行COPY命令将表中的数据导出到文件或从文件导入到表中时,会自动获取ACCESS SHARE锁模式 ANALYZE:当事务执行ANALYZE命令对表进行统计分析时,会自动获取ACCESS SHARE锁模式 VACUUM:当事务执行VACUUM命令对表进行回收空间时,会自动获取ACCESS SHARE锁模式 需要注意的是,ACCESS SHARE锁模式只能防止其他事务对整张表进行写入操作,但不能防止其他事务对表中某些行进行写入操作。如果需要保护表中某些行不被其他事务修改或删除,可以考虑使用ROW SHARE或其他更严格的锁模式 |
ROW SHARE | 与EXCLUSIVE和ACCESS EXCLUSIVE锁模式冲突。 | SELECT FOR UPDATE和SELECT FOR SHARE命令会在目标表上加此类型的锁。 ROW SHARE并不是排他性锁,其他事务仍然可以读取这个表的数据。 |
ROW EXCLUSIVE | 与SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE、ACCESS EXCLUSIVE锁模式冲突。 | UPDATE、DELETE、INSERT命令自动在所修改的表上请求加此类型的锁。通常情况下,修改表中数据的命令都是在表上加上此类型的锁。 |
SHARE UPDATE EXCLUSIVE | 与SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE、ACCESS EXCLUSIVE锁模式冲突。 | 模式改变和运行VACUUM并发的情况下,这种锁模式可以保护表。 VACUUM(不带FULL选项)、ANALYZE、CREATE INDEX CONCURRENTLY命令请求此类型的锁。 |
SHARE | 与ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE ROW EXCLUSIVE、EXCLUSIVE、ACCESS EXCLUSIVE锁模式冲突。 | 这种锁模式避免表的并发数据修改。 CREATE INDEX(不带CONCURRENTLY选项)语句要求这种锁模式 |
SHARE ROW EXCLUSIVE | 与ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE、ACCESS EXCLUSIVE锁模式冲突。 | 任何PG命令都不会自动请求这种锁模式。 |
EXCLUSIVE | 与ROW SHARE、ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE、ACCESS EXCLUSIVE锁模式冲突。这种锁模式只允许并发ACCESS SHARE锁,也就是说,只有对表的读操作可以和持有这个锁的事务并发执行。 | 任何PG命令都不会在用户表上自动请求这种锁模式。不过,在执行某些操作时,会在某些系统表上请求这种锁模式。 |
ACCESS EXCLUSIVE | 与所有的锁模式冲突(包括ACCESS SHARE、ROW SHARE、ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE)。 | 这种锁模式保证只能有一个用户访问此表。 在LOCK TABLE命令中没有明确声明需要的锁模式时,它是默认锁模式。 ALTER TABLE、DROP TABLE、TRUNCATE、REINDEX、CLUSTER、VACUUM FULL命令要求此类型的锁。 |
表级锁冲突矩阵
水平代表已申请到的锁模式竖直代表请求的锁模式 | ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE | SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE |
ACCESS SHARE | Y | Y | Y | Y | Y | Y | Y | N |
ROW SHARE | Y | Y | Y | Y | Y | Y | N | N |
ROW EXCLUSIVE | Y | Y | Y | Y | N | N | N | N |
SHARE UPDATE EXCLUSIVE | Y | Y | Y | N | N | N | N | N |
SHARE | Y | Y | N | N | Y | N | N | N |
SHARE ROW EXCLUSIVE | Y | Y | N | N | N | N | N | N |
EXCLUSIVE | Y | N | N | N | N | N | N | N |
ACCESS EXCLUSIVE | N | N | N | N | N | N | N | N |
- “N”表示这两种锁冲突,也就是说,同一用户不能同时持有这两种锁,“Y”表示两种锁可以兼容。
- 表级锁只有SHARE和EXCLUSIVE这两种,这两种锁基本上就是读写锁的意思。
- 加上SHARE锁后相当于加了读锁,表中的内容就不能变化了。可以为多个事务加上此锁,只要任意一个用户不释放此读锁,其他用户就不能修改这个表。(注意这里的维度是表)。
- 多版本功能下,如果改某一行的数据,实际上并没有改变原先那行数据,而是另复制出一个新行,修改都在新行上进行,事务进行不提交,别人是看不到这条数据的;由于原先那行数据没有变化,在修改过程中,读数据的人仍然可以读到原有数据,这样就没有必要阻塞其他用户读数据了。
- 多版本功能下,除了SHARE和EXCLUSIVE两个锁,还需要增加两个锁,一个叫作“ACCESS SHARE”,表明加上这个锁,即使是正在修改数据的情况下也允许读数据;另一个锁是“ACCESS EXCLUSION”,意思是即使有多版本的功能,也不允许访问数据。
表级锁加锁的对象是表,由于加锁的范围太大而导致并发不高,于是人们提出了行级锁的概念,但是行级锁与表级锁之间会产生冲突,这时就需要有一种机制来描述行级锁与表级锁之间的关系。在MySQL中是使用“意向锁”的概念来解决这一问题的,方法就是当我们要修改表中的某一行数据时,需要先在表上加一种锁,表示即将在表的部分行上加共享锁或排它锁。
PG中也是这样实现的,如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”;这4种锁的关系(意向锁与非意向之间的冲突矩阵)如下表所示:
X | S | IX | IS | |
X | N | N | N | N |
S | N | Y | N | Y |
IX | N | N | Y | Y |
IS | N | Y | Y | Y |
表级锁总计
- 共享锁“SHARE”
- 排它锁“EXCLUSIVE”
- ACCESS SHARE
- ACCESS EXCLUSIVE
- 意向共享锁“ROW SHARE”
- 意向排它锁“ROW EXCLUSIVE”
- SHARE UPDATE EXCLUSIVE
- SHARE ROW EXCLUSIVE
多版本原因,修改一条数据的同时允许读数据,所以为了处理这种情况,增加了“ACCESS”相关的两种锁;为了处理表锁和行锁之间的关系,于是有了意向锁的概念,增加了意向共享锁和意向排它锁;由于意向锁之间不会产生冲突,而且意向排它锁互相之间也不会产生冲突,于是又需要更严格一些的锁,这样就产生了SHARE UPDATE EXCLUSIVE和SHARE ROW EXCLUSIVE锁。
行级锁模式
行级锁模式比较简单,只有两种,即“共享锁”“排它锁”,或者可以说是“读锁”或“写锁”。
在PG中不称其为“读锁”的原因是,由于有多版本的实现,所以实际读取行数据时,并不会在行上执行任何锁(包括“读锁”)。