首页 > 数据库 >Postgresql锁

Postgresql锁

时间:2023-08-08 22:05:20浏览次数:348  
标签:EXCLUSIVE 事务 Postgresql 行锁 SHARE pgsql PostgreSQL

1.PostgreSQL锁基本概念

那么PostgreSQL数据库的表锁和行锁又有哪些呢?下面简单为大家介绍一下:

  1. 表锁:

在PostgreSQL数据库中,表级锁主要有SHARE(共享锁)和EXCLUSIVE(排他锁)。其中SHARE锁表示读锁,表中加了SHARE锁后,表的内容就不能变化了。我们可以在PostgreSQL中的多个事务中加SHARE锁,但是只要其中任意一个事务不进行释放,那么就没有任何事务可以修改这张表。而EXCLUSIVE锁表示写锁,表中加了EXCLUSIVE锁后,其他事务均无法对该表进行读写操作。

由于数据库中也存在多版本查询的概念。因此,在多版本的功能下,PostgreSQL中除了有SHARE锁和EXCLUSIVE锁以外,又新增了两个锁,其中一个是ACCESS SHARE锁,另一个是ACCESS EXCLUSIVE锁。其中,表中上了ACCESS SHARE锁,表示即使有事务正在修改数据,其他事务也可以读取数据。而ACCESS SHARE锁则表示表中上了该锁,即使数据库中存在多版本概念,其他事务也不能访问该表中的数据。

另外,我们也知道,表级锁的上锁对象是数据库中的表。在数据库中表锁往往会导致数据库高并发处理能力大大下降。因此,为了保证PostgreSQL中的高并发能力,数据库中也支持影响更小的行级锁。而行级锁和表级锁在PostgreSQL中则会产生一定的冲突。故数据库中也引入了意向锁的概念,即ROW SHARE和ROW EXCLUSIVE。这两个锁我们也叫做共享意向锁和排他意向锁。意向锁之间是不会发生冲突的,这时由于意向锁只是准备去给表上锁,但是实际上表中并没有进行上锁。但是意向锁和其他真实锁之间的关系和与真实锁之间的关系是相同的,比如SHARE锁和SHARE锁是相互冲突的,那么ROW SHARE和SHARE锁之间也是冲突的。

由于意向锁之间不会互相冲突,那么数据库中就必须出现一种更为严格的锁,即SHARE UPDATE EXCLUSIVE锁和SHARE ROW EXCLUSIVE锁,这种锁在自己之间同样会相互冲突。但是SHARE ROW EXCLUSIVE锁在当前的PostgreSQL中没有任何命令会使用。

总结一下上述内容,即PostgreSQL中总共有8中表锁。分别有SHARE、EXCLUSIVE、ACCESS SHARE、ACCESS EXCLUSIVE、ROW SHARE、ROW EXCLUSIVE、ROW UPDATE EXCLUSIVE、SHARE ROW EXCLUSIVE。

那么在PostgreSQL中有哪些命令会造成这8中锁呢?下面我们可以分别列举一下:

SHARE锁:CREATE INDEX语句会在数据库中请求该锁;

EXCLUSIVE锁:PostgreSQL中没有任何语句会请求该锁,但在一些操作中,会在系统表或者表上产生该锁,下面也会进行模拟示范。

ACCESS SHARE锁:select语句都会在表上请求该锁。

ACCESS EXCLUSIVE锁:alter table、drop table、truncate、reindex、cluster、VACUUM FULL、LOCK TABLE命令会向postgreSQL请求该锁。

ROW SHARE锁:select ...... for update和select....... for share会请求该锁。

ROW EXCLUSIVE锁:UPDATE、DELETE、INSERT命令会在所修改的表上请求该锁。

SHARE UPDATE EXCLUSIVE锁:VACUUM、ANALYZE、CREATE INDEX CONCURRENTLY命令会请求该锁。

SHARE ROW EXCLUSIVE锁:目前,没有命令会请求该锁。

  1. 行锁:

行锁分为共享锁和排它锁两种。但是由于PostgreSQL中多版本的概念,实际上在做select操作时,不会在行上加锁。

2.锁查看方法

那么在了解了PostgreSQL中的锁基本概念后,如何在实际情况下查看锁的信息,帮助我们进行一些问题的排查呢?

PostgreSQL中,提供了一个系统视图可以供我们对PostgreSQL中的锁进行查看,该视图即为pg_locks。

首先我们查看一下pg_locks的定义:

Postgresql锁_锁

其中,比较重要的几个字段有locktype、relation、page、tuple、virtualxid、transactionid、virtualtransaction、mode、granted。

Locktype,表示被锁定的对象类型;

Relation,表示锁定对象为表或者表的一部分时,会显示表的OID(通过::regclass转换可以转换为表名),否则为空;

Page,表示表中的页号,如果所对象不是表行或者表页,则为空;

Tuple,表示表中的页内的行号,如果锁定对象不是表行,则为空;

这里需要我们注意的一个点是,page和tuple表示了一张表中的页号和页号中的行号,那么这两个值就可以确定到一张表中的具体数据行,而这两个值得组合其实也是描述表中数据的一个字段ctid。

Vitualxid:代表虚拟事务ID,如果对象不是虚拟事务,则为空值,简称vxid。

Transactionid:代表事务ID,如果对象不是事务,则为空值,简称xid。

Virtualtransaction:持有或等待锁的虚拟事务ID。

Mode:表示锁的名称,即上一节中介绍的8个表锁类型。

Granted:表示对象是否持有锁,如果值为true,则表示当前对象持有该锁;如果值为false,则表示当前对象正在等待该锁。

这里还需要强调一个概念,就是事务和虚拟事务有什么区别?其实,在PostgreSQL中开启一个事务块,我们可能会在事务块中进行读操作,也可以进行写操作。当在事务块中进行读操作时(没有对数据进行实质性的变更),则为了节省资源,PostgreSQL只会给该SQL分配一个虚拟事务ID。当事务块中进行写操作时(对数据进行实质性的变更),PostgreSQL则会给SQL分配一个事务ID。

下面,我们分别在PostgreSQL中进行一系列测试,查看在PostgreSQL中操作产生的锁和现象。

  1. 表锁测试,观察产生的相关表锁

第一步,在session 1中通过lock table操作进行锁定表,但是必须要注意的是,该操作必须在事务块中执行,否则会报错。

  1. pgsql=# begin;  
  2. BEGIN  
  3. pgsql=# lock table t;  
  4. LOCK TABLE  

第二步,在session2中查询pg_locks,观察此时的锁信息。

  1. select locktype,relation::regclass,virtualxid,transactionid,virtualtransaction,pid,mode,granted   
  2. from pg_locks   
  3. where pid <>'5879';  

Postgresql锁_锁_02

可以看到,我们relation为t的锁,是我们进行lock table操作时获取的锁,此时锁的类型就为Access Exclusive’。

其实,在开启事务块后,未做任何操作时,postgresql只会分配一个vxid,并在该vxid上请求Exclusive锁。

当执行特殊lock table table_name,select txid_current的时候,首先会产生vxid,并请求Exclusive锁,然后会产生xid,并请求Exclusive锁,最后如果有锁表动作,则会产生AccessExclusive锁,如果没有其他动作,则不会有其他锁,这个时候我们可以在pg_locks中看到的锁至少会有两个,一个是vxid的Exclusive锁,另一个是xid的Exclusive锁(而在这个测试中有锁表动作,所以在t上有一个ACCESS EXCLUSIVE锁)。

此时,我们在session 3中,再次对t表进行锁表操作。

  1. pgsql=# begin;  
  2. BEGIN  
  3. pgsql=# lock table t;  
  4. LOCK TABLE  

然后在session 2中,再次查看锁表情况。

  1. pgsql=# select locktype,relation::regclass,virtualxid,transactionid,virtualtransaction,pid,mode,granted   
  2. from pg_locks   
  3. where pid <>'5879'; 

Postgresql锁_锁_03

可以看到,此时会话7139在t表也产生了一个AccessExclusive锁,但是granted字段中的值为false,说明此时该事务没有没有获取到这个排它锁,而是正在等待。

当我们在session 1中提交事务后,观察锁表信息。

Postgresql锁_锁_04

这时,我们看到这个事务的排他锁granted的值已经变成true,此时说明pgsql持有了这个锁。

  1. 行锁测试,并并确定行锁位置

第一步,在session1中执行select......for update语句产生行锁

  1. pgsql=# begin;  
  2. BEGIN  
  3. pgsql=#  select * from t save_point1 where id=1 for update;  

在session3中查询锁信息:

  1. pgsql=# select locktype,relation::regclass,virtualxid,transactionid,virtualtransaction,pid,mode,granted   
  2. from pg_locks   
  3. where pid <>'5879';  

Postgresql锁_锁_05

此时,可以看到加行锁会在t表上加一个表级意向锁,而且还在主键上加了AccessShare共享锁。但是在这个里面并没有显示行锁的消息。

在postgresql中,pg_locks并不能显示出每个行锁的信息,这是因为行锁信息不会记录到共享内存中。如果每个行锁都在内存中有一条记录的话,在对表做全表更新,表有多少行,就需要在内存中记录多少行所信息,那么内存有可能会吃不消。所以PostgreSQL被设计成在内存中不记录行锁信息。

当一个进程被另一个进程的行锁阻塞了,pg_locks有另一种信息来表示这种阻塞关系。第三步,在session 2上运行行锁语句。

  1. pgsql=# begin;  
  2. BEGIN  
  3. pgsql=#  select * from t save_point1 where id=1 for update; 

此时session 2会被阻塞,在session3中查看所信息。

Postgresql锁_PostgreSQL_06

此时可以发现,granted为false的xid正在等待sharelock。那么可以说明,pid为7139的进程申请一个类型为transactionid的锁时,被阻塞了,这个transactionid对应的xid是715。而从上面一条数据中可以看到,xid为715的锁被进程6625持有了(granted)为true。那么则可以知道PID为6625的会话即为行锁源。

通过这个测试,可以发现行锁中的阻塞信息是通过’transactiond’类型的锁体现出来的。从原理上来说,行锁是会在数据行上加自己的xid的,另一个进程读到这一行时,如果发现有行锁,会把行上另一个事务的xid读取出来,然后申请在这个xid上加’Share’锁。而持有行锁的进程已经在此xid上加了’Exclusive’锁,所以后面更新这行的进程会被阻塞。

所以如果查询因行锁被阻塞的进程信息,只需查询视图pg_locks中类型为’transactiond’的锁信息就可以了。

  1. 在PostgreSQL中当出现行锁,我们还可以确定该锁发生在哪一行数据。

第一步,session 1中执行update语句,但不提交事务。

  1. pgsql=# begin;  
  2. BEGIN  
  3. pgsql=# update save_point1 set name='ase' where id=4;  
  4. UPDATE 1  
  5. pgsql=#  

第二步,session 2中执行相同的update语句,此时,事务会被锁住。

  1. pgsql=# begin;  
  2. BEGIN  
  3. pgsql=# update save_point1 set name='ase' where id=4;  

此时,在session 3中查询锁的信息。

  1. pgsql=# select locktype,relation::regclass,page,tuple,virtualxid,transactionid,virtualtransaction,pid,mode,granted   
  2. pgsql-# from pg_locks   
  3. pgsql-# where pid <>'5879';  

Postgresql锁_PostgreSQL_07

从查询结果中,我们可以看到xid为724的7139会话正在等待xid为723的6625会话中的行锁。另外,我们可以看到查询结果中的page和tuple的组合参数为(0,13)。由于这个组合表示了表的ctid字段。则我们通过这个值来查询save_point1的值,则可以确定该行锁产生在哪一行。

  1. pgsql=# select * from save_point1 where ctid='(0,13)'; 

 Postgresql锁_锁_08

从查询结果可以知道,此时事务阻塞在save_point1的id=4,name=ase的值上了。这与我们进行update测试而未提交的行一致。

标签:EXCLUSIVE,事务,Postgresql,行锁,SHARE,pgsql,PostgreSQL
From: https://blog.51cto.com/u_13482808/7012476

相关文章

  • PostgreSQL数据库基本使用
    1.简介PGSQL是一个功能非常强大同时是开源的对象关系型数据库.经过二十几年的发展,PostgreSQL 是目前世界上可以获得的最先进的开放源码数据库系统。2.基本语法表是关系型数据库存储结构化数据的基本结构,可以使用CREATETABLE创建所需要的结构,其语法:CREATETABLEIFNOTEXISTS......
  • PostgreSQL-PITR 原理
    在 PostgreSQL的数据目录的pg_wal(10版本之前是pg_xlog子目录)子目录中始终维护一个WAL日志文件。该日志文件记录了数据库数据文件的每次改变。最初设计该日志文件的主要目的是为了数据库异常崩溃后,能够重放最后一次checkpoint点之后的日志文件,把数据库推到最终的一致状态,......
  • 最常见的 PostgreSQL 面试题
    PostgreSQL是众多可用数据库中最顶级、最强大和开源的数据库之一。它扩展了SQL语言,主要用作许多移动、Web和分析应用程序的主要数据仓库。对PostgreSQL认证专家的需求增长良好。因此,在这里我们上传了一些常见的PostgreSQL面试问题和答案,以帮助您pojie面试。通过这些Postgre......
  • PostgreSQL数据库版本升级
    PostgreSQL数据库版本升级Postgresql是一个非常活跃的社区开源项目,更新速度很快,每一次版本的更新都会积极的修复旧版本的BUG,性能上也会有不同幅度的提升。10之前的版本由三部分组成,10开始只有两部分数字组成。PostgreSQL版本发布规则,一年一个大版本,一个季度一个小版本;P......
  • postgresql满语句查询及灭杀
    1.查询慢语句查询当前库超过指定运行时长的语句--获取正在执行的sql进程select c.relname对象名称, l.locktype可锁对象的类型, l.pid进程id, l.mode持有的锁模式, l.granted是否已经对锁进行授权, l.fastpath, psa.datname数据库名称, psa.wait_event等待事件,......
  • PostgreSQL-unlogged table
    PostgreSQL中,有一种表的类型为unloggedtable,该种类型的表不会写入wal日志中,所以在写入的速度上比普通的堆表快很多,但是该表在数据库崩溃的时候,会被truncate,数据会丢失,而且该表也不支持流复制,所以在standby节点是无法查到该表数据的。在该表上创建的索引也是unlogged索引。创建un......
  • postgresql执行计划
    SQL优化主要从三个角度进行:(1)扫描方式;(2)连接方式;(3)连接顺序。如果解决好这三方面的问题,那么这条SQL的执行效率就基本上是靠谱的。看懂SQL的执行计划的关键也是要首先了解这三方面的基本概念,只有搞清楚了这些基本概念,才能够更好的看懂SQL的执行计划,下面我们分别来学习这些预备知识。......
  • PostgreSQL-错误处理集(持续更新... ...)
    1无法删除用户ERROR:角色不能被删除,因为有对象依赖于它LINE1:模式public的权限/用户下有表存在解决方法:收回赋予此用户的模式public的权限,并删除用户下的表,即可正常删除。postgres=#\duListofrolesRolename|......
  • postgresql 常用的删除重复数据方法
    一、最高效方法测试环境验证,6600万行大表,删除2200万重复数据仅需3分钟deletefromdeltestawherea.ctid=any(array(selectctidfrom(selectrow_number()over(partitionbyid),ctidfromdeltest)twheret.row_number>1)); --family_no相同的数据,保留i......
  • postgresql database basis
    postgresqlbasisthesystemcatalogtablesofpsotgresqlincludethefollowingtables:pg_databases:containsinformationabout alldatabases,suchasdatabasename,owner,characterset,etcpg_tablespace: containsinformationaboutalltablespaces,suc......