点击上方蓝字关注我们

点击上方蓝字关注我们




ACCESS SHARE:只与 ACCESS EXCLUSIVE 锁模式冲突。SELECT 命令在被引用的表上获得一个这种模式的锁。通常,任何只读取表而不修改它的查询都将获得这种模式的锁。
ROW SHARE:与 EXCLUSIVE 和 ACCESS EXCLUSIVE 锁模式冲突。SELECT FOR UPDATE 和SELECT FOR 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、REINDEX CONCURRENTLY、 CREATE STATISTICS 以及某些 ALTER INDEX 和 ALTER TABLE 的变体获得这种模式的锁。 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 锁模式冲突。这种模式保护一个表不受并发数据修改所影响,并且是自排他的,这样在一个时刻只能有一个会话持有它。CREATE TRIGGER 和某些形式的 ALTER TABLE 将获得这种模式的锁。 EXCLUSIVE:与 ROW SHARE、ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE 和 ACCESS EXCLUSIVE 锁模式冲突。这种模式只允许并发的 ACCESS SHARE 锁,即只有来自于表的读操作可以与一个持有该锁模式的事务并行处理。REFRESH MATERIALIZED VIEW CONCURRENTLY 将获得这种模式的锁。 ACCESS EXCLUSIVE:与所有模式的锁冲突(ACCESS SHARE、ROW SHARE、ROW EXCLUSIVE、SHARE UPDATE EXCLUSIVE、SHARE、SHARE ROW EXCLUSIVE、EXCLUSIVE 和 ACCESS EXCLUSIVE)。这种模式保证持有者是访问该表的唯一事务。ALTER TABLE、DROP TABLE、TRUNCATE、REINDEX、CLUSTER、VACUUM FULL和REFRESH MATERIALIZED VIEW(不带CONCURRENTLY)命令将获得这种模式的锁。很多形式的 ALTER INDEX 和 ALTER TABLE 也在这个层面上获得锁。这也是未显式指定模式的 LOCK TABLE 命令的默认锁模式。
--窗口 a
postgres=# begin;
BEGIN
--窗口 b
postgres=# select relation::regclass tab_name,mode from pg_locks where relation = 't'::regclass;
tab_name | mode
----------+------
(0 rows)
复制
--窗口 a
postgres=# begin;
BEGIN
postgres=*# select * from t; -->新执行的操作
a
---
1
(1 row)
--窗口 b
postgres=# select relation::regclass tab_name,mode from pg_locks where relation = 't'::regclass;
tab_name | mode
----------+-----------------
t | AccessShareLock -->新增的锁
(1 row)
复制
--窗口 a
postgres=# begin;
BEGIN
postgres=*# select * from t;
a
---
1
(1 row)
postgres=*# select * from t for update; -->新执行的操作
a
---
1
(1 row)
--窗口 b
postgres=# select relation::regclass tab_name,mode from pg_locks where relation = 't'::regclass;
tab_name | mode
----------+-----------------
t | AccessShareLock
t | RowShareLock -->新增的锁
(2 rows)
复制
--窗口 a
postgres=# begin;
BEGIN
postgres=*# select * from t;
a
---
1
(1 row)
postgres=*# select * from t for update;
a
---
1
(1 row)
postgres=*# update t set a = 2 where a = 1; -->新执行的操作
UPDATE 1
--窗口 b
postgres=# select relation::regclass tab_name,mode from pg_locks where relation = 't'::regclass;
tab_name | mode
----------+------------------
t | AccessShareLock
t | RowShareLock
t | RowExclusiveLock -->新增的锁
(3 rows)
复制
--窗口 a
postgres=# begin;
BEGIN
postgres=*# select * from t;
a
---
1
(1 row)
postgres=*# select * from t for update;
a
---
1
(1 row)
postgres=*# update t set a = 2 where a = 1;
UPDATE 1
postgres=*# create index idx_t_a on t(a); -->新执行的操作
CREATE INDEX
--窗口 b
postgres=# select relation::regclass tab_name,mode from pg_locks where relation = 't'::regclass;
tab_name | mode
----------+------------------
t | AccessShareLock
t | RowShareLock
t | RowExclusiveLock
t | ShareLock -->新增的锁
(4 rows)
复制
--窗口 a
postgres=# begin;
BEGIN
postgres=*# select * from t;
a
---
1
(1 row)
postgres=*# select * from t for update;
a
---
1
(1 row)
postgres=*# update t set a = 2 where a = 1;
UPDATE 1
postgres=*# create index idx_t_a on t(a);
CREATE INDEX
postgres=*# alter table t add b int; -->新执行的操作
ALTER TABLE
--窗口 b
postgres=# select relation::regclass tab_name,mode from pg_locks where relation = 't'::regclass;
tab_name | mode
----------+------------------
t | AccessShareLock
t | RowShareLock
t | RowExclusiveLock
t | ShareLock
t | AccessExclusiveLock -->新增的锁
(5 rows)
复制

FOR UPDATE:FOR UPDATE 会导致由 SELECT 语句检索到的行被锁定,就好像它们要被更新。这可以阻止它们被其他事务锁定、修改或者删除,一直到当前事务结束。也就是说其他尝试 UPDATE、DELETE、SELECT FOR UPDATE、SELECT FOR NO KEY UPDATE、SELECT FOR SHARE 或者 SELECT FOR KEY SHARE 将被阻塞,直到当前事务结束。反过来,SELECT FOR UPDATE 将等待已经在相同行上运行以上这些命令的并发事务,并且接着锁定并且返回被更新的行(或者没有行,因为行可能已被删除)。不过,在一个 REPEATABLE READ 或 SERIALIZABLE 事务中,如果一个要被锁定的行在事务开始后被更改,将会抛出一个错误。 FOR NO KEY UPDATE:行为与 FOR UPDATE 类似,不过获得的锁较弱,这种锁将不会阻塞尝试在相同行上获得锁的 SELECT FOR KEY SHARE 命令。任何不获取 FOR UPDATE 锁的 UPDATE 也会获得这种锁模式。 FOR SHARE:行为与 FOR NO KEY UPDATE 类似,不过它在每个检索到的行上获得一个共享锁而不是排他锁。一个共享锁会阻塞其他事务在这些行上执行 UPDATE、DELETE、SELECT FOR UPDATE 或者 SELECT FOR NO KEY UPDATE,但是它不会阻止它们执行 SELECT FOR SHARE 或者 SELECT FOR KEY SHARE。 FOR KEY SHARE:行为与 FOR SHARE 类似,不过锁较弱,SELECT FOR UPDATE 会被阻塞,但是 SELECT FOR NO KEY UPDATE 不会被阻塞。一个键共享锁会阻塞其他事务执行修改键值的 DELETE 或者 UPDATE,但不会阻塞其他 UPDATE,也不会阻止 SELECT FOR NO KEY UPDATE、SELECT FOR SHARE或者 SELECT FOR KEY SHARE。
--窗口 a
postgres=# begin;
BEGIN
postgres=*# select * from t for update;
a
---
1
(1 row)
--窗口 b
postgres=# select * from t for update;
^CCancel request sent
ERROR: canceling statement due to user request
CONTEXT: while locking tuple (0,1) in relation "t"
postgres=# select * from t for no key update;
^CCancel request sent
ERROR: canceling statement due to user request
CONTEXT: while locking tuple (0,1) in relation "t"
postgres=#
postgres=# select * from t for share;
^CCancel request sent
ERROR: canceling statement due to user request
CONTEXT: while locking tuple (0,1) in relation "t"
postgres=#
postgres=# select * from t for key share;
^CCancel request sent
ERROR: canceling statement due to user request
CONTEXT: while locking tuple (0,1) in relation "t"
复制
--窗口 a
postgres=# begin;
BEGIN
postgres=*# select * from t for key share;
a
---
1
(1 row)
--窗口 b
postgres=# select * from t for update;
^CCancel request sent
ERROR: canceling statement due to user request
CONTEXT: while locking tuple (0,1) in relation "t"
postgres=#
postgres=# select * from t for no key update;
a
---
1
(1 row)
postgres=# select * from t for share;
a
---
1
(1 row)
postgres=# select * from t for key share;
a
---
1
(1 row)
复制
--窗口 a
postgres=# begin;
BEGIN
postgres=*# update t1 set a = 2 where a = 1;
UPDATE 1
postgres=*# update t2 set a = 11 where a = 1;
UPDATE 1
--窗口 b
postgres=# begin;
BEGIN
postgres=*# update t2 set a = 2 where a = 1;
UPDATE 1
postgres=*# update t1 set a = 22 where a = 1;
ERROR: deadlock detected -->此处触发了死锁自动处理机制,中断了该事务。
DETAIL: Process 11771 waits for ShareLock on transaction 231938; blocked by process 11547.
Process 11547 waits for ShareLock on transaction 231940; blocked by process 11771.
HINT: See server log for query details.
CONTEXT: while updating tuple (0,4) in relation "t1"
复制
会话级别:对于每一个完成的锁请求必须有一个相应的解锁请求,直至锁被真正释放。 事务级别:在事务结束时会自动释放它们,并且没有显式的解锁操作。


--窗口 a
postgres=# create table t1(id int primary key);
CREATE TABLE
postgres=#
postgres=# insert into t1 values(1);
INSERT 0 1
postgres=#
postgres=# begin;
BEGIN
postgres=*# delete from t1 where id = 1;
DELETE 1
postgres=*# insert into t1 values(1);
INSERT 0 1
postgres=*#
--窗口 b
postgres=# begin;
BEGIN
postgres=*# delete from t1 where id = 1;
DELETE 0 -->窗口 a 事务未提交之前一直等待,窗口 a 事务提交后,显示删除 0 行。
postgres=*# insert into t1 values(1);
ERROR: duplicate key value violates unique constraint "t1_pkey"
DETAIL: Key (id)=(1) already exists. -->插入数据报主键唯一约束错误。
复制
--窗口 a
postgres=# begin;
BEGIN
postgres=*# select pg_try_advisory_xact_lock(1); -->窗口 a 进行操作前获取咨询锁
pg_try_advisory_xact_lock
---------------------------
t
(1 row)
postgres=*# delete from t1 where id =1;
DELETE 1
postgres=*# insert into t1 values(1);
INSERT 0 1
postgres=*# commit;
COMMIT
--窗口 b
postgres=# begin;
BEGIN
postgres=*# select pg_try_advisory_xact_lock(1); --窗口 b 进行操作前获取咨询锁失败,不能进行下一步操作。
pg_try_advisory_xact_lock
---------------------------
f
(1 row)
postgres=*# select pg_try_advisory_xact_lock(1); --窗口 b 在窗口 a 提交事务后获取咨询锁成功,可进行下一步操作。
pg_try_advisory_xact_lock
---------------------------
t
(1 row)
postgres=*# delete from t1 where id =1;
DELETE 1
postgres=*# insert into t1 values(1);
INSERT 0 1
postgres=*# commit;
COMMIT
postgres=# select * from t1;
id
----
1
(1 row)
复制

LOCK [ TABLE ] [ ONLY ] name [ * ] [, ...] [ IN lockmode MODE ] [ NOWAIT ]
-- 其中 lockmode 可以是以下之一:
ACCESS SHARE | ROW SHARE | ROW EXCLUSIVE | SHARE UPDATE EXCLUSIVE
| SHARE | SHARE ROW EXCLUSIVE | EXCLUSIVE | ACCESS EXCLUSIVE
复制
name:要锁定的一个现有表的名称(可以是模式限定的)。如果在表名前指定了 ONLY,只有该表会被锁定。如果没有指定了 ONLY,该表和它所有的后代表(如果有)都会被锁定。可选地,在表名后指定 * 来显式地表示把后代表包括在内。命令LOCK TABLE a, b 等效于 LOCK TABLE a; LOCK TABLE b。这些表会被按照在 LOCK TABLE中指定的顺序一个一个被锁定。 lockmode:锁模式指定这个锁和哪些锁冲突。如果没有指定锁模式,那将使用最严格的模式 ACCESS EXCLUSIVE。 NOWAIT:指定 LOCK TABLE 不等待任何冲突锁被释放:如果所指定的锁不能立即获得,那么事务就会中止。
--在将要执行一次删除操作前在主键表上取一个 SHARE ROW EXCLUSIVE 锁
BEGIN WORK;
LOCK TABLE films IN SHARE ROW EXCLUSIVE MODE;
DELETE FROM films_user_comments WHERE id IN
(SELECT id FROM films WHERE rating < 5);
DELETE FROM films WHERE rating < 5;
COMMIT WORK;
复制

--查询持锁
SELECT
lock2.pid as locking_pid,
lock1.pid as locked_pid,
stat1.usename as locked_user,
stat1.query as locked_statement,
stat1.state as state,
stat2.query as locking_statement,
stat2.state as state,
now() - stat1.query_start as locking_duration,
stat2.usename as locking_user
FROM pg_catalog.pg_locks lock1
JOIN pg_catalog.pg_stat_activity stat1 on lock1.pid = stat1.pid
JOIN pg_catalog.pg_locks lock2 on
(lock1.locktype,lock1.database,lock1.relation,
lock1.page,lock1.tuple,lock1.virtualxid,
lock1.transactionid,lock1.classid,lock1.objid,
lock1.objsubid) IS NOT DISTINCT FROM
(lock2.locktype,lock2.DATABASE,
lock2.relation,lock2.page,
lock2.tuple,lock2.virtualxid,
lock2.transactionid,lock2.classid,
lock2.objid,lock2.objsubid)
JOIN pg_catalog.pg_stat_activity stat2 on lock2.pid
= stat2.pid
WHERE NOT lock1.granted AND lock2.granted;
--查询行锁
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;
--查询超过 5s 的长事务
select
usename,
datname,
state,
wait_event_type,
wait_event,
xact_start,
now(),query,pid,
now()-xact_start as time
from
pg_stat_activity
where
now()-xact_start>interval '5 sec'
and query !~ '^COPY' and state<>'idle'
order by xact_start;
复制
--session消失,事物回退,需要superuser权限,可以关闭所有的后台进程
select pg_terminate_backend(1445);
--session还在,事物回退,只能关闭自己用户下的后台进程
select pg_cancel_backend(1445);
--批量取消生成取消的SQL
WITH trans AS
(SELECT pid
FROM pg_stat_activity
WHERE now()-xact_start>interval '10 sec'
AND query !~ '^COPY'
AND STATE<>'idle'
ORDER BY xact_start)
SELECT 'select pg_cancel_backend' || '(' || trans.pid || ');'AS killsql
FROM trans;
WITH trans AS
(SELECT pid
FROM pg_stat_activity
WHERE now()-xact_start>interval '10 sec'
AND query !~ '^COPY'
AND STATE<>'idle'
ORDER BY xact_start)
SELECT 'select pg_terminate_backend' || '(' || trans.pid || ');'AS killsql
FROM trans;
复制

文章转载自KunlunBase 昆仑数据库,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。
评论
相关阅读
王炸!OGG 23ai 终于支持从PostgreSQL备库抽取数据了
曹海峰
401次阅读
2025-03-09 12:54:06
玩一玩系列——玩玩login_hook(一款即将停止维护的PostgreSQL登录插件)
小满未满、
375次阅读
2025-03-08 18:19:28
明明想执行的SQL是DELETE、UPDATE,但为什么看到的是SELECT(FDW的实现原理解析)
小满未满、
352次阅读
2025-03-19 23:11:26
PostgreSQL初/中/高级认证考试(3.15)通过考生公示
开源软件联盟PostgreSQL分会
304次阅读
2025-03-20 09:50:36
IvorySQL 4.4 发布 - 基于 PostgreSQL 17.4,增强平台支持
通讯员
198次阅读
2025-03-20 15:31:04
套壳论
梧桐
188次阅读
2025-03-09 10:58:17
命名不规范,事后泪两行
xiongcc
179次阅读
2025-03-13 14:26:08
晨章数据三款分布式数据库产品全面开源,以开放向AI时代进发
晨章数据
145次阅读
2025-03-10 17:10:07
PG vs MySQL 执行计划解读的异同点
进击的CJR
122次阅读
2025-03-21 10:50:08
版本发布| IvorySQL 4.4 发布
IvorySQL开源数据库社区
115次阅读
2025-03-13 09:52:33