PG的锁(二)

太阳2年前技术文章621

四、死锁

PostgreSQL自动检测死锁情况并会自动回滚其中一个事务进行处理,从而其他事务完成。

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)


事务一

事务二

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;//被夯住

update t1 set info='cccccccc' where id=2; //返回报错

update执行成功

commit //自动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;


五、咨询锁

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)
标签: PostgreSQL
返回列表

上一篇:PG的锁(一)

下一篇:PG的锁(三)

相关文章

PG体系结构(一)

PG体系结构(一)

一、进程结构PG数据库启动时会先启动一个主进程(9.3之前称为postmaster,9.3以后称为postgres server process),然后fork出一些辅助子进程(backend、bac...

PG的pathman分区表工具

一、概述在PG<=10的版本中,都是通过表继承的方式进行分区的,必须使用CHECK CONSTRAINT将每个分区创建为子表 。PostgreSQL 10提供了本机分区,它与经典方法没有什么不同...

PG的锁(一)

一、表级锁1.1 表级锁模式常见锁模式以及应用场景:ACCESS SHARE :select操作获取该模式锁资源,通常情况下所有只读取不修改表的查询都会获取该模式锁资源ROW SHARE : sele...

PG的多版本并发控制(二)

PG的多版本并发控制(二)

二、 PG数据库DML操作的相关概念xmin、xmax、cmin、cmax是每个数据行tuple上的隐藏字段,主要用于区别不同事务以及相同事务内tuple的行版本。在了解这四个参数概念前,我们首先需要...

PG的多版本并发控制(一)

PG的多版本并发控制(一)

一、 表系统字段几个比较重要概念1.1  tupletuple表示表中的数据行,在MySQL中用row表示。在表数据页中,主要分为普通的数据元祖和TOAST元祖。以下是一个普通数据元祖的结构,主要由三...

PG的表膨胀

1 什么是表膨胀众所周知,PostgreSQL的多版本并发是通过保留变更前的记录来实现的。当数据记录被DML修改,旧版本记录仍保留不变,仅仅需要修改相关记录的xmin、xmax属性,并新增写入变更后的...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。