PG数据库中有表级锁与行级锁两种锁。当执行查询、插入、更新、删除表数据等操作时,会先获得表上的锁,然后在获得行上的锁。
表级锁
表级锁模式
| 锁模式 | 解释 |
|---|---|
| ACCESS SHARE | 只与ACCESS EXCLUSIVE模式冲突。SELECT命令在所引用的表上加此类型的锁。通常情况下,任何只读取表而不修改表的查询都会请求这种模式 |
| ROW SHARE | 与EXCLUSIVE和ACCESS EXCLUSIVE锁模式冲突。SELECT FOR UPDATE和SELECTFOR 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(不带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 | 与所有锁模式冲突。该锁保证只有持有锁的事务能够访问当前表。ALTER TABLE、DROP TABLE、TRUNCATE、REINDEX、CLUSTER、VACUUM FULL命令要求此类型的锁。在LOCK TABLE命令中没有明确声明需要的锁模式时,它是默认锁模式 |
为什么有这么多表级锁
PG中一共有8中表锁,最普通的锁是共享锁"SHARE"和排他锁"EXCLUSIVE",加上了SHARE锁后,只要任意一个用户不释放此锁,其他用户就不能修改这个表。叫上EXCLUSIVE后,其他进程即不能写也不能读这条数据。由于PG存在多版本的功能,即修改某一行数据,实际上并没有修改原先那条数据,而是复制一个新行,修改都在新行上进行。由于事务未提交无法查询,在修改过程中读数据仍然可以读到原有数据,所以就没有必要阻塞其他用户读数据了。故增加了"ACCESS SHARE"锁,即使正在修改数据的情况下也允许读数据,还有一个"ACCESS EXCLUSIVE"锁,意思是即使有多版本的功能,也不允许访问数据。
为了处理表锁和行锁的关系(行级锁和表级锁会产生冲突),加了意向共享锁"ROW SHARE"和意向排他锁"ROW EXCLUSIVE"两种锁,当我们要修改表中某一行的数据时,需要现在表上加种锁,表示即将在表的部分行上加共享锁或者排他锁。
由于意向锁之间不会产生冲突,并且意向排他锁之间也不会产生冲突,所以又添加了两种更严格的锁"SHARE UPDATE EXCLUSIVE","SHARE ROW EXCLUSIVE"
行级锁模式
行级锁比较简单,只有两种,即"共享锁","排它锁"。
死锁
什么是死锁
死锁是指两个或两个以上的事务在执行过程中相互持有对方期待的锁,如没有其他机制,它们都将无法进行下去。例如事务1在表A上持有一个排它锁,同时试图请求一个在表B上的排他锁,而事务2已经持有了表B的排它锁,同时在请求表A上的一个排它锁,那么两个事务同时无法执行下去。PG能自动监测死锁,然后退出其中一个事务,但哪个事务被退出无法预测。
死锁发生的四个必要条件
-
互斥条件:指事务所分配到的资源加了排它锁,即在一段时间只能由一个事务加锁占用。如果此时还有其他进程请求排它锁,则请求者只能等待。
-
请求和保持条件:事务持有了一把排它锁,但请求其他排它锁,但其他排它锁被占用,此时请求等待,但同时该事务读自己的排它锁持有不放。
-
不剥夺条件:只事务已获得的锁仅能由自己释放,其他进程无法剥夺。
-
环路等待请求:指发生死锁时,必然有一个事务请求的环形链。
预防死锁的最好方法通常是保证使用一个数据库的应用都以相同的顺序在多个对象上请求排它锁。比如事务按B->C->A->D顺序修改这四张表,但某个事务先修改了A,若想修改C,那么必须回滚先前对A表的更新,先更新C表,再更新A表。
参考《PostgreSQL修炼之道 从小工到专家 第2版》