锁
简介
锁用于解决并发环境下的数据一致性问题。当一个事务在进行操作时会对操作的数据加锁,从二线制另一个事务的操作。为了保证效率,加锁的力度不宜太大。
锁的类型
表级锁
Postgres 的表级锁有8种模式,并且存储在共享内存中。
ACESS SHARE
- 允许并发的
SELECT
查询。这是最宽松的锁模式。持有ACCESS SHARE
锁的事务可以读取表中的数据,而不会阻止其他事务读取或修改(除非是ACCESS EXCLUSIVE
锁) SELECT
语句默认获取此锁- 与
ACCESS EXCLUSIVE
冲突
- 允许并发的
ROW SHARE
- 允许并发读写,但不允许对表进行并发的 DDL(数据定义语言)操作,并阻止获取
EXCLUSIVE
和ACCESS EXCLUSIVE
锁。通常在对行进行锁定(FOR UPDATE
或FOR SHARE
)时获取,表示该事务可能稍后会修改某些行。 SELECT FOR UPDATE
,SELECT FOR NO KEY UPDATE
,SELECT FOR SHARE
,SELECT FOR KEY SHARE
语句获取此锁(同时也会获取行级锁)。UPDATE
和DELETE
在获取行锁之前也会获取此锁。- 与
EXCLUSIVE
,ACCESS EXCLUSIVE
冲突
- 允许并发读写,但不允许对表进行并发的 DDL(数据定义语言)操作,并阻止获取
ROW EXCLUSIVE
- 允许并发读,但不允许其他事务获取
ROW SHARE
,SHARE UPDATE EXCLUSIVE
,SHARE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
,ACCESS EXCLUSIVE
锁。防止其他事务对表进行并发的行级锁定或更高等级的锁定,但允许其他事务进行SELECT
,INSERT
,UPDATE
,DELETE
操作(这些操作自身也会获取ROW EXCLUSIVE
锁)。用于标记表正在被修改。 INSERT
,UPDATE
,DELETE
语句获取此锁- 与
SHARE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
,ACCESS EXCLUSIVE
冲突
- 允许并发读,但不允许其他事务获取
SHARE UPDATE EXCLUSIVE
- 允许并发读写,但不允许其他事务获取
SHARE UPDATE EXCLUSIVE
,SHARE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
,ACCESS EXCLUSIVE
锁。主要用于防止并发的 DDL 操作或VACUUM FULL
,但允许正常的读写操作。VACUUM
和ANALYZE
等维护操作会获 取此锁 VACUUM
(非FULL
),ANALYZE
,CREATE INDEX CONCURRENTLY
获取此锁- 与
SHARE UPDATE EXCLUSIVE
,SHARE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
,ACCESS EXCLUSIVE
冲突
- 允许并发读写,但不允许其他事务获取
SHARE
- 允许并发读,但不允许任何写操作以及
ROW EXCLUSIVE
和更高级别的锁。通常用于保护对表进行大型操作(如CREATE INDEX
)时,防止表被并发修改 CREATE INDEX
(非CONCURRENTLY
) 获取此锁- 与
ROW EXCLUSIVE
,SHARE UPDATE EXCLUSIVE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
,ACCESS EXCLUSIVE
冲突
- 允许并发读,但不允许任何写操作以及
SHARE ROW EXCLUSIVE
- 允许并发读,但不允许任何写操作以及
ROW EXCLUSIVE
,SHARE UPDATE EXCLUSIVE
,SHARE
,EXCLUSIVE
,ACCESS EXCLUSIVE
锁。比SHARE
更严格,阻止所有并发的写和大多数并发的读写相关锁 - 不由常见 DML/DDL 隐式获取,通常通过
LOCK TABLE ... IN SHARE ROW EXCLUSIVE MODE
显式获取 - 与
ROW EXCLUSIVE
,SHARE UPDATE EXCLUSIVE
,SHARE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
,ACCESS EXCLUSIVE
冲突
- 允许并发读,但不允许任何写操作以及
EXCLUSIVE
- 阻止除了
ACCESS SHARE
(读) 之外的所有锁。持有EXCLUSIVE
锁的事务可以读取表,但阻止任何写操作或其他类型的锁(除了简单的读取) REFRESH MATERIALIZED VIEW
(非CONCURRENTLY
) 获取此锁。也可通过LOCK TABLE ... IN EXCLUSIVE MODE
显式获取- 与
ROW SHARE
,ROW EXCLUSIVE
,SHARE UPDATE EXCLUSIVE
,SHARE
,SHARE ROW EXCLUSIVE
,EXCLUSIVE
,ACCESS EXCLUSIVE
冲突
- 阻止除了
ACCESS EXCLUSIVE
- 阻止所有类型的锁,包括
ACCESS SHARE
。这是最严格的锁模式,持有此锁的事务对表拥有完全排他性的访问权限,其他任何事务都不能对该表进行任何操作(读或写) - 大多数 DDL 命令(如
DROP TABLE
,ALTER TABLE
,TRUNCATE
,REINDEX
,CLUSTER
,VACUUM FULL
)获取此锁 - 与所有其他锁模式冲突
- 阻止所有类型的锁,包括
查看表锁信息
-- 1. 先获取表的OID,假设OID为10000
select oid from pg_class where relname='your_table_name';
-- 2. 查看表锁信息
select * from pg_locks where relation = 10000;
行级锁
行级锁不影响数据查询,只阻塞对同一行的写入者和加锁者。
行级锁有4种模式
FOR UPDATE
FOR NO KEY UPDATE
FOR SHARE
FOR KEY SHARE
死锁
死锁是指两个或两个以上的事务在执行过程中因互相等待或争抢锁资源而造成的互相等待的现象。
3个相关设置参数
deadlock_timeout
:进行死锁检测之前在一个锁上等待的总时间lock_timeout
:锁等待的超时时间statement_timeout
:控制语句的执行时长
常见避免死锁的方式
- 大事务改小事务。大事务更容易产生死锁
- 按固定顺序访问表和行
- 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减小发生死锁的概率。