PG lock

One, table-level lock

1.1 Table-level lock mode

Common lock modes and application scenarios:

  • ACCESS SHARE: The select operation obtains the mode lock resource. Under normal circumstances, all queries that only read and do not modify the table will obtain the mode lock resource
  • ROW SHARE: select for update and select for share commands to obtain the lock resource of this mode
  • ROW EXCLUSIVE: DML operation will acquire the mode lock resource, usually any operation that needs to modify the table data will hold the mode lock resource
  • SHARE UPDATE EXCLUSIVE: For vacuum (not full), create index concurrnetly, reindex concurrently, create statistics, alter index related, alter table related operations will hold this mode lock resource, this mode lock resource is mainly for the scope of concurrency on the same table DDL changes and vacuum operations
  • SHARE: The create index (non-concurrently) operation will hold the mode lock resource, which can prevent concurrent index creation operations on the table
  • SHARE ROW EXCLUSIVE: cerate trigger, some alter table operations will hold this mode lock resource
  • EXCLUSIVE: refresh materialized view concurrently operation will hold the mode lock resource
  • ACCESS EXCLUSIVE: lock table mode lock mode, drop table, truncate, reindex, cluster, vacuum full, refresh materialized view (without concurrently) There are also some alter table, alter index operations that need to obtain the lock resources of this mode, and the lock resources of this mode are guaranteed The holder only accesses the table.

1.2 Table-level lock compatibility

Lock mode ACCESS SHARE ROW SHARE ROW EXCLUSIVE SHARE UPDATE EXCLUSIVE SHARE SHARE ROW EXCLUSIVE EXCLUSIVE ACCESS EXCLUSIVE
ACCESS SHARE X
ROW SHARE X X
ROW EXCLUSIVE X X X X
SHARE UPDATE EXCLUSIVE X X X X X
SHARE X X X X X
SHARE ROW EXCLUSIVE X X X X X X
EXCLUSIVE X X X X X X X
ACCESS EXCLUSIVE X X X X X X X X

Two, row-level lock

2.1 Row-level lock mode

Common lock modes and application scenarios:

  • FOR UPDATE: For all for update operations, for update row locks are performed on the retrieved data rows to prevent other transactions from updating records that hold for update row locks. In the RR and SERIALIZABLE isolation levels, if a row locked by for update is modified after the current transaction starts, the transaction will throw an exception. For update and delete operations, it is also necessary to obtain the row mode lock for update
  • FOR NO KEY UPDATE: Similar to for update row mode lock, but its lock range is relatively weak. For all delete and update operations that do not need to acquire for update row lock resources, the row mode lock is held. In the RR and SERIALIZABLE isolation levels, if a row locked by for update is modified after the current transaction starts, the transaction will throw an exception.
  • FOR SHARE: Add share lock to retrieve records. This mode lock resource will block other transactions from deleting, updating, select for update, for no key update, but allowing other transactions to concurrently add for share or for key share
  • FOR KEY SHARE: Compared to for share, this mode lock is relatively weaker, and it allows other transactions to concurrently hold for no key update mode lock resources

2.2 Row-level lock compatibility

Lock mode FOR KEY SHARE FOR SHARE FOR NO KEY UPDATE FOR UPDATE
FOR KEY SHARE X
FOR SHARE X X
FOR NO KEY UPDATE X X X
FOR UPDATE X X X X

2.3 Lock test

1. For update test under RR isolation level

Business one Business two
- START TRANSACTION ISOLATION LEVEL REPEATABLE READ;
- select id from t1 where id2=327;//id=2,6240
START TRANSACTION ISOLATION LEVEL REPEATABLE READ; -
- update t1 set info =‘aaaaaaaaa’ where id=2;
select * from t1 where id2=327 for update;//被夯住 -
- commit;
select for update throws an exception error "ERROR: could not serialize access due to concurrent update"

The same error message will also appear for the for no key update operation.

Three, page-level lock

  • page-level share/exclusive: Controls the read and write operations of data pages in the shared buffer pool, and releases the page-level locks immediately after obtaining the corresponding rows that need to be queried or updated. Application developers usually do not need to care about page-level locks.

Four, deadlock

PostgreSQL automatically detects the deadlock situation and automatically rolls back one of the transactions for processing, so that the other transactions are completed.

db1=# select * from t1 where id in (1,2,3);
 id | id2  |               info               |          crt_time
----+------+----------------------------------+----------------------------
  2 |  327 | aaaaaaaaa                        | 2020-10-12 16:20:50.278564
  3 | 6098 | 8dc6d07062720d35f7824dceea5a3ef9 | 2020-10-12 17:20:50.278572
(2 rows)
Business one Business two
begin; begin;
update t1 set info=‘bbbbbbbbb’ where id=3; update t1 set info=‘bbbbbbbbb’ where id=2;
- update t1 set info='cccccccc' where id=3;//It is tamped
update t1 set info='cccccccc' where id=2; //return error update executed successfully
commit //Auto rollback commit;
## 事务一update报错内容
ERROR:  deadlock detected
DETAIL:  Process 26174 waits for ShareLock on transaction 1738606; blocked by process 26178.
Process 26178 waits for ShareLock on transaction 1738607; blocked by process 26174.
HINT:  See server log for query details.
CONTEXT:  while updating tuple (51,123) in relation "t1"

## 事务二提交后数据变更结果
db1=# select * from t1 where id in (1,2,3);
 id | id2  |   info    |          crt_time
----+------+-----------+----------------------------
  2 |  327 | bbbbbbbbb | 2020-10-12 16:20:50.278564
  3 | 6098 | cccccccc  | 2020-10-12 17:20:50.278572
(2 rows)

## server log详细内容报错
2020-10-15 16:53:03.688 CST [26174] ERROR:  deadlock detected
2020-10-15 16:53:03.688 CST [26174] DETAIL:  Process 26174 waits for ShareLock on transaction 1738606; blocked by process 26178.
	Process 26178 waits for ShareLock on transaction 1738607; blocked by process 26174.
	Process 26174: update t1 set info='cccccccc' where id=2;
	Process 26178: update t1 set info='cccccccc' where id=3;
2020-10-15 16:53:03.688 CST [26174] HINT:  See server log for query details.
2020-10-15 16:53:03.688 CST [26174] CONTEXT:  while updating tuple (51,123) in relation "t1"
2020-10-15 16:53:03.688 CST [26174] STATEMENT:  update t1 set info='cccccccc' where id=2;

Five, consulting lock

5.1 什么是咨询锁

咨询锁是一种具有应用层面的锁,数据库中一般不会主动调用相关咨询锁资源,需要开发同学在应用层决定是否需要使用这类锁。咨询锁使用的模型是悲观锁模型,相对于传统意义上的锁,咨询锁更快,可以避免表膨胀,并在会话结束时由服务器自动清除。

咨询锁可分为会话/事务级别:

  • 会话级别的锁需要显式释放,随着连接的关闭自动释放
  • 事务级别的锁也需要显式释放,或者会随着事务的结束(提交或者回滚)一并释放

锁级别:

  • _lock : 会话级别锁
  • _xact_lock :事务级别锁

排它锁/共享锁的获取以及释放:

  • _lock() : 获取指定参数下的会话/事务级别排它锁资源
  • _lock_shared() : 获取指定参数下的会话/事务级别共享锁资源
  • _unlock() : 释放指定参数下的会话级别排它锁资源
  • _unlock_shared() : 释放指定参数下的会话级别共享锁资源
  • _unlock_all() : 释放当前会话下持有的所有会话级别锁资源

锁获取模式:

  • pg_try_advisory : 若无法获取资源则一直等待
  • pg_advisory : 若无法获取资源,直接返回false,不会一直等待

5.2 咨询锁相关函数

函数名称 返回类型 描述
pg_advisory_lock(key bigint) void session级别排他锁
pg_advisory_lock(key1 int, key2 int) void session级别排他锁
pg_advisory_lock_shared(key bigint) void session级别共享锁
pg_advisory_lock_shared(key1 int, key2 int) void session级别共享锁
pg_advisory_unlock(key bigint) boolean 释放session级别排他锁
pg_advisory_unlock(key1 int, key2 int) boolean 释放session级别排他锁
pg_advisory_unlock_all() void 释放本session持有的所有session级别的锁
pg_advisory_unlock_shared(key bigint) boolean 释放session级别共享锁
pg_advisory_unlock_shared(key1 int, key2 int) boolean 释放session级别共享锁
pg_advisory_xact_lock(key bigint) void 获取事务级别排他锁
pg_advisory_xact_lock(key1 int, key2 int) void 获取事务级别排他锁
pg_advisory_xact_lock_shared(key bigint) void 获取事务级别共享锁
pg_advisory_xact_lock_shared(key1 int, key2 int) void 获取事务级别共享锁
pg_try_advisory_lock(key bigint) boolean 试图获取session级别排他锁,成功返回true,否则返回false
pg_try_advisory_lock(key1 int, key2 int) boolean 试图获取session级别排他锁,成功返回true,否则返回false
pg_try_advisory_lock_shared(key bigint) boolean 试图获取session级别共享锁,成功返回true,否则返回false
pg_try_advisory_lock_shared(key1 int, key2 int) boolean 试图获取session级别共享锁,成功返回true,否则返回false
pg_try_advisory_xact_lock(key bigint) boolean 试图获取事务级别排他锁,成功返回true,否则返回false
pg_try_advisory_xact_lock(key1 int, key2 int) boolean 试图获取事务级别排他锁,成功返回true,否则返回false
pg_try_advisory_xact_lock_shared(key bigint) boolean 试图获取事务级别共享锁,成功返回true,否则返回false
pg_try_advisory_xact_lock_shared(key1 int, key2 int) boolean 试图获取事务级别共享锁,成功返回true,否则返回false

锁分为64位和32位,bigint表示64位、int表示32位。对于同一行记录,若分为使用64位、32位资源锁进行加锁,是不会互相干预的,他们是不同空间上的锁。

5.3 资源锁测试

1、库粒度的资源锁

## 会话一 请求对t2表的id=2进行添加锁,加锁成功。由于pg_try_advisory_lock指定参数为id,其实时在数据库级别添加了锁资源
db1=# select pg_try_advisory_lock(id),id from t2 where id=2;
 pg_try_advisory_lock | id
----------------------+----
 t                    |  2
(1 row)

## 会话二 请求对t1表的id=2进行加锁,加锁失败
db1=# select pg_try_advisory_lock(id),id from t1 where id=2;
 pg_try_advisory_lock | id
----------------------+----
 f                    |  2
(1 row)

## 会话一 释放已申请锁资源
db1=# select pg_advisory_unlock(id),id from t2 where id = 2;
 pg_advisory_unlock | id
--------------------+----
 t                  |  2
(1 row)

## 会话二 再次尝试申请t1表id=2的锁资源,加锁成功
db1=# select pg_try_advisory_lock(id),id from t1 where id=2;
 pg_try_advisory_lock | id
----------------------+----
 t                    |  2
(1 row)

2、行粒度的资源锁

## 会话一尝试对t1表的id=2记录获取资源锁,获取成功
db1=# select pg_try_advisory_lock(cast('t1'::regclass::oid as int),id),id from t1 where id=2;
 pg_try_advisory_lock | id
----------------------+----
 t                    |  2
(1 row)

## 会话二尝试对t1表的id=2记录获取资源锁,获取失败
db1=# select pg_try_advisory_lock(cast('t1'::regclass::oid as int),id),id from t1 where id=2;
 pg_try_advisory_lock | id
----------------------+----
 f                    |  2
(1 row)

## 会话二尝试对t1表的id=2记录获取资源锁,获取成功
db1=# select pg_try_advisory_lock(cast('t1'::regclass::oid as int),id),id from t1 where id=3;
 pg_try_advisory_lock | id
----------------------+----
 t                    |  3
(1 row)

六、锁的维护

6.1 锁相关参数

  • deadlock_timeout(integer):默认1s,表示pg数据库仅对锁超时大于1s的情况进行死锁检测。
  • log_lock_waits : 默认关闭,若打开该参数则表示会将锁超时超过deadlock_timeout的信息记录到日志中。
  • lock_timeout : 锁等待超时,默认为0表示禁用锁超时。若该参数设置>0则表示会话等待锁资源Ns后如果仍无法获取到相关锁资源则终止相关语句执行。
  • idle_in_transaction_session_timeout : 对于一个空闲的事务,超过多少时间后自动终止,单位为毫秒。默认为0表示禁用该参数。会话终止会释放该会话所有锁资源。

锁相关参数:https://www.postgresql.org/docs/12/runtime-config-locks.html

6.2 锁监控

1、PG中锁相关的两个重要的视图

2、查看谁锁了谁

当一个进程处于等待(被堵塞)状态时,是谁干的?可以使用如下函数,快速得到捣蛋(堵塞别人)的PID。

1)请求锁时被堵,是哪些PID堵的?

pg_blocking_pids(int) int[] Process ID(s) that are blocking specified server process ID from acquiring a lock

2)请求safe快照时被堵(SSI隔离级别,请求安全快照冲突),是哪些PID堵的?

pg_safe_snapshot_blocking_pids(int) int[] Process ID(s) that are blocking specified server process ID from acquiring a safe snapshot

示例:

select pid,pg_blocking_pids(pid),wait_event_type,wait_event,query from pg_stat_activity ;

- pid : 当前会话,被阻塞者
- pg_blocking_pids(pid) : 阻塞者
- wait_event_type : 会话状态,Lock表示被阻塞无法获取锁资源
- wait_event : 等待事件
- query : 会话执行相关查询
事务一 事务二 事务三
begin; begin -
update t1 set info =‘aaaaaaaaabbbbbbbbb’ where id=2; - -
- update t1 set info =‘aaaaaaaaa’ where id=2;//被夯住 -
- - select pid,pg_blocking_pids(pid),wait_event_type,wait_event,query from pg_stat_activity ;
- update执行成功, info =‘aaaaaaaaa’ -
commit//执行报错 commit -
## 事务三查看锁等待
## 可以看到28363会话正在等待27152会话
db1=# select pid,pg_blocking_pids(pid),wait_event_type,wait_event,query from pg_stat_activity ;
  pid  | pg_blocking_pids | wait_event_type |     wait_event      |                                           query
-------+------------------+-----------------+---------------------+-------------------------------------------------------------------------------------------
  9020 | {}               | Activity        | LogicalLauncherMain |
  9017 | {}               | Activity        | AutoVacuumMain      |
 28363 | {27152}          | Lock            | transactionid       | update t1 set info ='aaaaaaaaa' where id=2;
 27219 | {}               |                 |                     | select pid,pg_blocking_pids(pid),wait_event_type,wait_event,query from pg_stat_activity ;
 27152 | {}               | Client          | ClientRead          | update t1 set info ='aaaaaaaaabbbbbbbbb' where id=2;
  9015 | {}               | Activity        | BgWriterHibernate   |
  9014 | {}               | Activity        | CheckpointerMain    |
  9016 | {}               | Activity        | WalWriterMain       |
(8 rows)


## 事务一commit相关报错
db1=# commit;
FATAL:  terminating connection due to administrator command
server closed the connection unexpectedly
	This probably means the server terminated abnormally
	before or while processing the request.
The connection to the server was lost. Attempting reset: Succeeded.

2、通过数据库日志(开启lock_timeout, log_lockwait参数)(csvlog)跟踪锁等待信息

3、通过SQL查看锁冲突具体信息

1)锁冲突查询SQL

with    
t_wait as    
(    
  select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.granted,   
  a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid,a.transactionid,a.fastpath,    
  b.state,b.query,b.xact_start,b.query_start,b.usename,b.datname,b.client_addr,b.client_port,b.application_name   
    from pg_locks a,pg_stat_activity b where a.pid=b.pid and not a.granted   
),   
t_run as   
(   
  select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.granted,   
  a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid,a.transactionid,a.fastpath,   
  b.state,b.query,b.xact_start,b.query_start,b.usename,b.datname,b.client_addr,b.client_port,b.application_name   
    from pg_locks a,pg_stat_activity b where a.pid=b.pid and a.granted   
),   
t_overlap as   
(   
  select r.* from t_wait w join t_run r on   
  (   
    r.locktype is not distinct from w.locktype and   
    r.database is not distinct from w.database and   
    r.relation is not distinct from w.relation and   
    r.page is not distinct from w.page and   
    r.tuple is not distinct from w.tuple and   
    r.virtualxid is not distinct from w.virtualxid and   
    r.transactionid is not distinct from w.transactionid and   
    r.classid is not distinct from w.classid and   
    r.objid is not distinct from w.objid and   
    r.objsubid is not distinct from w.objsubid and   
    r.pid <> w.pid   
  )    
),    
t_unionall as    
(    
  select r.* from t_overlap r    
  union all    
  select w.* from t_wait w    
)    
select locktype,datname,relation::regclass,page,tuple,virtualxid,transactionid::text,classid::regclass,objid,objsubid,   
string_agg(   
'Pid: '||case when pid is null then 'NULL' else pid::text end||chr(10)||   
'Lock_Granted: '||case when granted is null then 'NULL' else granted::text end||' , Mode: '||case when mode is null then 'NULL' else mode::text end||' , FastPath: '||case when fastpath is null then 'NULL' else fastpath::text end||' , VirtualTransaction: '||case when virtualtransaction is null then 'NULL' else virtualtransaction::text end||' , Session_State: '||case when state is null then 'NULL' else state::text end||chr(10)||   
'Username: '||case when usename is null then 'NULL' else usename::text end||' , Database: '||case when datname is null then 'NULL' else datname::text end||' , Client_Addr: '||case when client_addr is null then 'NULL' else client_addr::text end||' , Client_Port: '||case when client_port is null then 'NULL' else client_port::text end||' , Application_Name: '||case when application_name is null then 'NULL' else application_name::text end||chr(10)||    
'Xact_Start: '||case when xact_start is null then 'NULL' else xact_start::text end||' , Query_Start: '||case when query_start is null then 'NULL' else query_start::text end||' , Xact_Elapse: '||case when (now()-xact_start) is null then 'NULL' else (now()-xact_start)::text end||' , Query_Elapse: '||case when (now()-query_start) is null then 'NULL' else (now()-query_start)::text end||chr(10)||    
'SQL (Current SQL in Transaction): '||chr(10)||  
case when query is null then 'NULL' else query::text end,    
chr(10)||'--------'||chr(10)    
order by    
  (  case mode    
    when 'INVALID' then 0   
    when 'AccessShareLock' then 1   
    when 'RowShareLock' then 2   
    when 'RowExclusiveLock' then 3   
    when 'ShareUpdateExclusiveLock' then 4   
    when 'ShareLock' then 5   
    when 'ShareRowExclusiveLock' then 6   
    when 'ExclusiveLock' then 7   
    when 'AccessExclusiveLock' then 8   
    else 0   
  end  ) desc,   
  (case when granted then 0 else 1 end)  
) as lock_conflict  
from t_unionall   
group by   
locktype,datname,relation,page,tuple,virtualxid,transactionid::text,classid,objid,objsubid ;  

2)如果觉得写SQL麻烦,可以将它创建为视图,直接查询视图

create view v_locks_monitor as   
with    
t_wait as    
(    
  select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.granted,   
  a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid,a.transactionid,a.fastpath,    
  b.state,b.query,b.xact_start,b.query_start,b.usename,b.datname,b.client_addr,b.client_port,b.application_name   
    from pg_locks a,pg_stat_activity b where a.pid=b.pid and not a.granted   
),   
t_run as   
(   
  select a.mode,a.locktype,a.database,a.relation,a.page,a.tuple,a.classid,a.granted,   
  a.objid,a.objsubid,a.pid,a.virtualtransaction,a.virtualxid,a.transactionid,a.fastpath,   
  b.state,b.query,b.xact_start,b.query_start,b.usename,b.datname,b.client_addr,b.client_port,b.application_name   
    from pg_locks a,pg_stat_activity b where a.pid=b.pid and a.granted   
),   
t_overlap as   
(   
  select r.* from t_wait w join t_run r on   
  (   
    r.locktype is not distinct from w.locktype and   
    r.database is not distinct from w.database and   
    r.relation is not distinct from w.relation and   
    r.page is not distinct from w.page and   
    r.tuple is not distinct from w.tuple and   
    r.virtualxid is not distinct from w.virtualxid and   
    r.transactionid is not distinct from w.transactionid and   
    r.classid is not distinct from w.classid and   
    r.objid is not distinct from w.objid and   
    r.objsubid is not distinct from w.objsubid and   
    r.pid <> w.pid   
  )    
),    
t_unionall as    
(    
  select r.* from t_overlap r    
  union all    
  select w.* from t_wait w    
)    
select locktype,datname,relation::regclass,page,tuple,virtualxid,transactionid::text,classid::regclass,objid,objsubid,   
string_agg(   
'Pid: '||case when pid is null then 'NULL' else pid::text end||chr(10)||   
'Lock_Granted: '||case when granted is null then 'NULL' else granted::text end||' , Mode: '||case when mode is null then 'NULL' else mode::text end||' , FastPath: '||case when fastpath is null then 'NULL' else fastpath::text end||' , VirtualTransaction: '||case when virtualtransaction is null then 'NULL' else virtualtransaction::text end||' , Session_State: '||case when state is null then 'NULL' else state::text end||chr(10)||   
'Username: '||case when usename is null then 'NULL' else usename::text end||' , Database: '||case when datname is null then 'NULL' else datname::text end||' , Client_Addr: '||case when client_addr is null then 'NULL' else client_addr::text end||' , Client_Port: '||case when client_port is null then 'NULL' else client_port::text end||' , Application_Name: '||case when application_name is null then 'NULL' else application_name::text end||chr(10)||    
'Xact_Start: '||case when xact_start is null then 'NULL' else xact_start::text end||' , Query_Start: '||case when query_start is null then 'NULL' else query_start::text end||' , Xact_Elapse: '||case when (now()-xact_start) is null then 'NULL' else (now()-xact_start)::text end||' , Query_Elapse: '||case when (now()-query_start) is null then 'NULL' else (now()-query_start)::text end||chr(10)||    
'SQL (Current SQL in Transaction): '||chr(10)||  
case when query is null then 'NULL' else query::text end,    
chr(10)||'--------'||chr(10)    
order by    
  (  case mode    
    when 'INVALID' then 0   
    when 'AccessShareLock' then 1   
    when 'RowShareLock' then 2   
    when 'RowExclusiveLock' then 3   
    when 'ShareUpdateExclusiveLock' then 4   
    when 'ShareLock' then 5   
    when 'ShareRowExclusiveLock' then 6   
    when 'ExclusiveLock' then 7   
    when 'AccessExclusiveLock' then 8   
    else 0   
  end  ) desc,   
  (case when granted then 0 else 1 end)  
) as lock_conflict  
from t_unionall   
group by   
locktype,datname,relation,page,tuple,virtualxid,transactionid::text,classid,objid,objsubid ;  

3)锁冲突视图查询事务查询

-[ RECORD 1 ]-+------------------------------------------------------------------------------------------------------------------------------------------------------
locktype      | transactionid
datname       | db1
relation      |
page          |
tuple         |
virtualxid    |
transactionid | 1738627
classid       |
objid         |
objsubid      |
lock_conflict | Pid: 28363                                                                                                                                           +
              | Lock_Granted: true , Mode: ExclusiveLock , FastPath: false , VirtualTransaction: 3/1372985 , Session_State: idle in transaction                      +
              | Username: postgres , Database: db1 , Client_Addr: NULL , Client_Port: -1 , Application_Name: psql                                                    +
              | Xact_Start: 2020-10-19 22:22:40.71729+08 , Query_Start: 2020-10-19 22:22:53.991278+08 , Xact_Elapse: 00:00:57.879438 , Query_Elapse: 00:00:44.60545  +
              | SQL (Current SQL in Transaction):                                                                                                                    +
              | select pg_backend_pid();                                                                                                                             +
              | --------                                                                                                                                             +
              | Pid: 31024                                                                                                                                           +
              | Lock_Granted: false , Mode: ShareLock , FastPath: false , VirtualTransaction: 6/1244302 , Session_State: active                                      +
              | Username: postgres , Database: db1 , Client_Addr: NULL , Client_Port: -1 , Application_Name: psql                                                    +
              | Xact_Start: 2020-10-19 22:23:02.130503+08 , Query_Start: 2020-10-19 22:23:24.715491+08 , Xact_Elapse: 00:00:36.466225 , Query_Elapse: 00:00:13.881237+
              | SQL (Current SQL in Transaction):                                                                                                                    +
              | update t1 set info ='cccccc' where id=2;

4)通过锁冲突视图查询,已经清晰看到每一个发生了锁等待的对象,按锁的大小排序展示出来。只需要找到terminate最大的锁对应的PID即可。

db1=# select pg_terminate_backend(28363);   //找到terminate最大的PID并进行kill
 pg_terminate_backend
----------------------
 t
(1 row)

db1=# \x 1
Expanded display is on.
db1=# select * from v_locks_monitor ;       //锁冲突恢复
(0 rows)

4、Lock trace,通过审计日志分析事务锁等待情况

1)开启审计日志

log_destination = 'csvlog'  
logging_collector = on  
log_truncate_on_rotation = on  
log_statement = 'all'  

2)psql登录到对应数据库中,挂一个打印锁等待的窗口

select * from v_locks_monitor;      //锁冲突查询视图
\watch 0.2                          //查询间隔

3)tail 挂一个日志观测窗口

for ((i=1;i>0;i=1)); do grep RowExclusiveLock *.csv ; sleep 0.2; done  
或  
for ((i=1;i>0;i=1)); do grep acquired *.csv ; sleep 0.2; done  

4)通过步骤二发现阻塞会话PID,然后根据PID信息在步骤三中的审计日志查看具体的相关操作,对锁阻塞进行具体分析

>>文档参考<<

咨询锁文档博客:https://www.cnblogs.com/wy123/archive/2020/08/14/13499526.html
官方文档:https://www.postgresql.org/docs/13/explicit-locking.html
PostgreSQL 谁堵塞了谁(锁等待检测):https://developer.aliyun.com/article/746217

Guess you like

Origin blog.csdn.net/weixin_37692493/article/details/109174717