跳到主要内容

简介

锁用于解决并发环境下的数据一致性问题。当一个事务在进行操作时会对操作的数据加锁,从二线制另一个事务的操作。为了保证效率,加锁的力度不宜太大。

锁的类型

表级锁

Postgres 的表级锁有8种模式,并且存储在共享内存中。

  • ACESS SHARE
    • 允许并发的 SELECT 查询。这是最宽松的锁模式。持有 ACCESS SHARE 锁的事务可以读取表中的数据,而不会阻止其他事务读取或修改(除非是 ACCESS EXCLUSIVE 锁)
    • SELECT 语句默认获取此锁
    • ACCESS EXCLUSIVE 冲突
  • ROW SHARE
    • 允许并发读写,但不允许对表进行并发的 DDL(数据定义语言)操作,并阻止获取 EXCLUSIVEACCESS EXCLUSIVE 锁。通常在对行进行锁定(FOR UPDATEFOR SHARE)时获取,表示该事务可能稍后会修改某些行。
    • SELECT FOR UPDATE, SELECT FOR NO KEY UPDATE, SELECT FOR SHARE, SELECT FOR KEY SHARE 语句获取此锁(同时也会获取行级锁)。UPDATEDELETE 在获取行锁之前也会获取此锁。
    • EXCLUSIVE, ACCESS EXCLUSIVE 冲突
  • 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,但允许正常的读写操作。VACUUMANALYZE 等维护操作会获取此锁
    • 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:控制语句的执行时长

常见避免死锁的方式

  • 大事务改小事务。大事务更容易产生死锁
  • 按固定顺序访问表和行
  • 在同一个事务中,尽可能做到一次锁定所需要的所有资源,减小发生死锁的概率。