pg锁介绍
AccessShareLock:是共享读锁,任何只读取表而不修改它的查询都将获得这种锁模式,此时不允许DDL
ExclusiveLock:文档没找到合适的解释,猜测是全局资源排他锁,对应每个事务的资源排他锁
RowShareLock:select for update ,select for share情况下会出现此锁,此时不允许DML和DDL
RowExclusiveLock:dml会产生此锁,此时不允许dml相同记录,也不允许DDL
AccessExclusiveLock: create、ALTER TABLE、DROP TABLE、TRUNCATE、REINDEX、CLUSTER、VACUUM FULL和REFRESH MATERIALIZED VIEW(不带CONCURRENTLY) 表会产生此锁。这种模式保证持有者是访问该表的唯一事务。此锁会阻塞查询
ShareUpdateExclusiveLock:由VACUUM(不带FULL)、ANALYZE、CREATE INDEX CONCURRENTLY、CREATE STATISTICS和ALTER TABLE VALIDATE以及其他ALTER TABLE的变体获得此锁,这种模式保护一个表不受并发模式改变和VACUUM运行的影响。
ShareLock:CREATE INDEX (WITHOUT CONCURRENTLY)会获得此锁,这种模式保护一个表不受并发数据改变的影响,即不能修改此表相关的数据和表结构。
ShareRowExclusiveLock:这种模式保护一个表不受并发数据修改所影响,并且是自排他的,这样在一个时刻只能有一个会话持有它。
AccessShareLock
场景一
session 1;
wjz=# begin;
BEGIN
wjz=# select * from test;
id
1
3
6
2
4
5
(6 rows)
session 2;
select a.pid,b.mode,a.query from pg_stat_activity a left join pg_locks b on a.pid=b.pid;
pid | mode | query
-------±----------------±-----------------------------------------------------------------------------------------
14906 | AccessShareLock | select * from test;
14906 | ExclusiveLock | select * from test;
发现查询除了AccessShareLock 外还有ExclusiveLock 。
RowShareLock
场景二
session 1;
wjz=# begin;
BEGIN
wjz=# select * from test for update;
id
1
3
6
2
4
5
(6 rows)
session 2;
postgres=# select a.pid,b.mode,a.query from pg_stat_activity a left join pg_locks b on a.pid=b.pid order by a.pid ;
pid | mode | query
-------±----------------±----------------------------------------------------------------------------------------------
14906 | RowShareLock | select * from test for update;
14906 | ExclusiveLock | select * from test for update;
14906 | ExclusiveLock | select * from test for update;
session 1;
wjz=# begin;
BEGIN
wjz=# select * from test for share;
id
1
3
6
2
4
5
(6 rows)
session 2;
postgres=# select a.pid,b.mode,a.query from pg_stat_activity a left join pg_locks b on a.pid=b.pid order by a.pid ;
pid | mode | query
-------±----------------±----------------------------------------------------------------------------------------------
14906 | RowShareLock | select * from test for share;
14906 | ExclusiveLock | select * from test for share;
14906 | ExclusiveLock | select * from test for share;
session 3;
wjz=# begin;
BEGIN
wjz=# update test set id=2 where id<3;
在select for share下也是无法DML和DDL这些表,此处也有ExclusiveLock。
RowExclusiveLock
场景三
session 1;
wjz=# begin;
BEGIN
wjz=# update test set id=3 where id<5;
UPDATE 4
session 2;
postgres=# select a.pid,b.mode,a.query from pg_stat_activity a left join pg_locks b on a.pid=b.pid order by a.pid ;
pid | mode | query
-------±-----------------±---------------------------------------------------------------------------------------------
14906 | RowExclusiveLock | update test set id=3 where id<5;
14906 | ExclusiveLock | update test set id=3 where id<5;
14906 | ExclusiveLock | update test set id=3 where id<5;
session 1;
wjz=# begin;
BEGIN
wjz=# insert into test values(10);
INSERT 0 1
wjz=#
session 2;
postgres=# select a.pid,b.mode,a.query from pg_stat_activity a left join pg_locks b on a.pid=b.pid order by a.pid ;
pid | mode | query
-------±-----------------±---------------------------------------------------------------------------------------------
14906 | RowExclusiveLock | insert into test values(10);
14906 | ExclusiveLock | insert into test values(10);
14906 | ExclusiveLock | insert into test values(10);
session 1;
wjz=# begin;
BEGIN
wjz=# delete from test where id<2;
DELETE 1
session 2;
postgres=# select a.pid,b.mode,a.query from pg_stat_activity a left join pg_locks b on a.pid=b.pid order by a.pid ;
pid | mode | query
-------±-----------------±---------------------------------------------------------------------------------------------
14906 | RowExclusiveLock | delete from test where id<2;
14906 | ExclusiveLock | delete from test where id<2;
14906 | ExclusiveLock | delete from test where id<2;
AccessExclusiveLock
场景四
session 1;
wjz=# begin;
BEGIN
wjz=# create table student(name varchar(10));
CREATE TABLE
wjz=# begin;
BEGIN
wjz=# alter table test add column name varchar(20);
ALTER TABLE
postgres=# select a.pid,b.mode,a.query from pg_stat_activity a left join pg_locks b on a.pid=b.pid order by a.pid ;
pid | mode | query
-------±--------------------±------------------------------------------------------------------------------------------
14906 | ExclusiveLock | create table student(name varchar(10));
14906 | AccessExclusiveLock | create table student(name varchar(10));
14906 | ExclusiveLock | create table student(name varchar(10));
14906 | AccessShareLock | create table student(name varchar(10));
14906 | ExclusiveLock | alter table test add column name varchar(20);
14906 | ExclusiveLock | alter table test add column name varchar(20);
14906 | AccessExclusiveLock | alter table test add column name varchar(20);
session 3;
wjz=# begin;
BEGIN
wjz=# update test set id=2 where id<3; —出现锁等待
ShareUpdateExclusiveLock
场景五
session 1;
wjz=# CREATE INDEX CONCURRENTLY idx_test1 on test(name) ;
CREATE INDEX
session 2;
postgres=# select a.pid,b.mode,a.query from pg_stat_activity a left join pg_locks b on a.pid=b.pid order by a.pid ;
pid | mode | query
-------±-------------------------±-------------------------------------------------------------------------------------
14906 | RowExclusiveLock | CREATE INDEX CONCURRENTLY idx_test1 on test(name) ;
14906 | RowExclusiveLock | CREATE INDEX CONCURRENTLY idx_test1 on test(name) ;
14906 | RowExclusiveLock | CREATE INDEX CONCURRENTLY idx_test1 on test(name) ;
14906 | ExclusiveLock | CREATE INDEX CONCURRENTLY idx_test1 on test(name) ;
14906 | AccessExclusiveLock | CREATE INDEX CONCURRENTLY idx_test1 on test(name) ;
14906 | ExclusiveLock | CREATE INDEX CONCURRENTLY idx_test1 on test(name) ;
14906 | ShareUpdateExclusiveLock | CREATE INDEX CONCURRENTLY idx_test1 on test(name) ;
不加concurrently关键字的DDL会阻塞dml和查询等
14906 | ExclusiveLock | create index idx_test2 on test(name) ;
14906 | AccessExclusiveLock | create index idx_test2 on test(name) ;
14906 | ExclusiveLock | create index idx_test2 on test(name) ;
14906 | ShareLock | create index idx_test2 on test(name) ;
session 3;
wjz=# select * from test; —发生等待
ShareRowExclusiveLock
这个锁才疏学浅,没有模拟出来,后续遇到再补充。
官网锁兼容性表格:
实验总结:
(1)ExclusiveLock这个锁出现在每个会话中,从字面意思理解是互斥锁,但从实验中发现此锁对表的其他锁不造成冲突,应该是负责管理事务本身资源方面的锁的占用。
(2)AccessShareLock与ShareUpdateExclusiveLock都不阻塞读写,但会阻塞DDL。
(3)AccessExclusiveLock会阻塞读写,但与ShareUpdateExclusiveLock共同存在下,不阻塞读写(具体原因估计和锁的优先级或者加锁顺序有关)。
(4)RowExclusiveLock:一般是DML产生的,但DDL某些情况下也会产生。
postgresql锁的资料网上太少了,测试完还是晕乎乎的,掌握一下锁出问题的解决办法吧!
查看等待锁的SQL:
select a.pid,a.usename,a.query,a.wait_event_type,b.mode,now()-a.xact_start as xid_startime from pg_stat_activity a join pg_locks b on a.pid=b.pid where not b.granted order by xid_startime asc;
查看当前数据库执行SQL情况
select datname,pid,usename,application_name,client_addr,now()-xact_start as xid_startime,now()-query_start as query_startime,now()-state_change as state_changetime,wait_event_type,state,query from pg_stat_activity where query !=’’ order by xid_startime asc;
查询阻塞源的函数
select pg_blocking_pids(23224);
取消阻塞源的函数:
select pg_terminate_backend(14906); 可释放阻塞源,这个相当于杀掉会话进程
select PG_CANCEL_BACKEND(14906);取消后台操作,回滚未提交事务,但不会杀掉会话进程