【赵渝强老师】PostgreSQL锁的类型

简介: PostgreSQL通过表级锁和行级锁实现并发控制,结合MVCC机制保障数据一致性。锁模式多样,粒度精细,可有效避免事务冲突,提升并发性能。

b411.png

在并发环境下为了解决并发一致性问题保证事务的隔离性,PostgreSQL采用了锁的机制。当一个事务在进行操作时会对操作的数据进行加锁,从而限制另一个事务的操作。为保证效率和想,加锁的粒度不宜太大。加锁的意义就在于当多个会话同时访问数据库的同一数据时,为所有会话提供高效的数据访问,并同时维护严格的数据一致性,从而实现数据的多版本并发控制。

多版本并发控制的英文名称是Multiversion Concurrency Control,简称MVCC。
它是指每个SQL语句看到的都只是当前事务开始的数据快照,而不用去关心底层数据的当前状态。
这样可以保护语句不会看到可能由其他在相同数据行上执行更新的并发事务造成的不一致数据,
为每一个数据库会话提供事务隔离。MVCC避免了传统的数据库系统的锁定方法,
将通过锁争夺最小化的方法来达到多会话并发访问时的性能最大化目的。


PostgreSQL提供了多种类型的锁模式用于控制对表中数据的并发访问。在这些锁当中最主要的是表级锁与行级锁,此外还有页级锁、咨询锁等等。下面将重点介绍表级锁与行级锁。视频讲解如下:


一、 表级锁

表级锁通常会在执行各种命令执行时自动获取,或者通过在事务中使用lock语句显式获取,每种表级锁都有自己的冲突集合。两个事务在同一时刻不能在同一个表上持有属于相互冲突模式的锁,但可以持有不冲突的锁。PostgreSQL的表级锁总共有八种模式,并存储在PostgreSQL的共享内存中。下表详细说明了这八种PostgreSQL表级锁。

image.png


这八种表级锁彼此之间存在一定的冲突,下表说明了它们之间的冲突关系。

image.png


在PostgreSQL数据库中可以通过pg_locks系统视图查询表上已经获取到的表级锁信息,下面通过一个具体的示例来说明。

(1)在会话一中查看当前数据库中的表,并查看表testtable1的结构。

postgres=# \d
           List of relations
 Schema |    Name    | Type  |  Owner   
--------+------------+-------+----------
 public | testtable1 | table | postgres
(1 row)
postgres=# \d testtable1;
             Table "public.testtable1"
 Column |  Type   | Collation | Nullable | Default 
--------+---------+-----------+----------+---------
 tid    | integer |           | not null | 
 tname  | text    |           |          | 
Indexes:
    "testtable1_pkey" PRIMARY KEY, btree (tid)
Tablespace: "mydemotbs"


(2)查看表testtable1的oid。

postgres=# select oid,relname,relkind,relfilenode from pg_class
postgres-# where relname ='testtable1';
  oid  |  relname   | relkind | relfilenode 
-------+------------+---------+-------------
 16395 | testtable1 | r       |       16395
(1 row)


(3)在会话一中开启一个事务并执行一条update语句。

postgres=# start transaction;
postgres=*# update testtable1 set tname='Tom123' where tid=1;
# 这里执行的事务没有结束。


(4)在会话二中更改表testtable1的结构,如:添加一个新的列。

postgres=# alter table testtable1 add dno int;
# 此时会话二的操作将会被阻塞。


(5)在会话三中查看表testtable1上的锁信息。

postgres=# \x
postgres=# select * from pg_locks where relation = 16395;
# 这里的“\x”表示将输出结果进行竖式显示。
# 输出的结果如下:
-[ RECORD 1 ]------+--------------------
locktype           | relation
database           | 13580
relation           | 16395
page               | 
tuple              | 
virtualxid         | 
transactionid      | 
classid            | 
objid              | 
objsubid           | 
virtualtransaction | 4/401
pid                | 41381
mode               | AccessExclusiveLock
granted            | f
fastpath           | f
-[ RECORD 2 ]------+--------------------
locktype           | relation
database           | 13580
relation           | 16395
page               | 
tuple              | 
virtualxid         | 
transactionid      | 
classid            | 
objid              | 
objsubid           | 
virtualtransaction | 3/644
pid                | 40920
mode               | RowExclusiveLock
granted            | t
fastpath           | f
# 从输出的结果可以看出,此时表testtable1上有两把表级锁,
# 分别是:AccessExclusiveLock和RowExclusiveLock。
# 但此时并不能观察到会话之间的阻塞。


(6)执行语句检查锁的等待,即:会话之间的阻塞信息。

postgres=# 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; 
# 输出的结果如下:
-[ RECORD 1 ]-+------------------------------------------------------
locktype      | relation                                             
datname       | postgres                                             
relation      | testtable1                                           
page          |                                                      
tuple         |                                                      
virtualxid    |                                                      
transactionid |                                                      
classid       |                                                      
objid         |                                                      
objsubid      |                                                      
lock_conflict 
| Pid: 41381                                           
              | Lock_Granted: false , Mode: AccessExclusiveLock , ...
          | SQL (Current SQL in Transaction):                    
              | alter table testtable1 add dno int;                  
              | --------                                             
              | Pid: 40920                                           
              | Lock_Granted: true , Mode: RowExclusiveLock , ...    
          | SQL (Current SQL in Transaction):                    
              | update testtable1 set tname='Tom123' where tid=1;    
# 通过这里输出的信息可以看到阻塞的进程ID号,以及发生等待的SQL语句。


(7)在会话一执行结束事务操作。

postgres=*# commit;
# 此时会话二将成功执行。


(8)重新在会话三中查看表testtable1上的锁信息,此时将没有任何锁的信息输出。

postgres=# select * from pg_locks where relation = 16395;


二、 行级锁

同一个事务可能会在相同的行上保持冲突的锁。但是除此之外,两个事务永远不可能在相同的行上持有冲突的锁。PostgreSQL行级锁不影响数据查询,它们只阻塞对同一行的写入者和加锁者。行级锁在事务结束时或保存点回滚的时候释放,就像表级锁一样。PostgreSQL行级锁支持以下四种不同的模式,如下表所示。

image.png


这四种行级锁彼此之间存在一定的冲突,下表说明了它们之间的冲突关系。

image.png


×表示存在相互之间的冲突。


在PostgreSQL中可以通过查询pg_locks的系统视图来获取行级锁的相关信息。下面展示了该系统视图的结构。

postgres=# \d pg_locks;
                   View "pg_catalog.pg_locks"
       Column       |   Type   | Collation | Nullable | Default 
--------------------+----------+-----------+----------+---------
 locktype           | text     |           |          | 
 database           | oid      |           |          | 
 relation           | oid      |           |          | 
 page               | integer  |           |          | 
 tuple              | smallint |           |          | 
 virtualxid         | text     |           |          | 
 transactionid      | xid      |           |          | 
 classid            | oid      |           |          | 
 objid              | oid      |           |          | 
 objsubid           | smallint |           |          | 
 virtualtransaction | text     |           |          | 
 pid                | integer  |           |          | 
 mode               | text     |           |          | 
 granted            | boolean  |           |          | 
 fastpath           | boolean  |           |          |



相关实践学习
使用PolarDB和ECS搭建门户网站
本场景主要介绍如何基于PolarDB和ECS实现搭建门户网站。
阿里云数据库产品家族及特性
阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
相关文章
|
6月前
|
存储 SQL 关系型数据库
【赵渝强老师】使用mydumper备份MySQL
本文介绍了使用mydumper工具进行MySQL数据库备份与恢复的操作方法。相比单线程工作的mysqldump,mydumper支持多线程,速度提升可达10倍。其功能包括事务性表快照、快速压缩、导出binlog等,并提供详细的参数说明和操作步骤。文章通过实例演示了安装mydumper、创建存储目录、全库备份、指定数据库及表备份、删除数据库以及使用myloader恢复数据的完整流程,并附带视频讲解,帮助用户更好地理解和应用该工具。
366 0
|
20天前
|
机器学习/深度学习 人工智能 自然语言处理
构建AI智能体:三十八、告别“冷启动”:看大模型如何解决推荐系统的世纪难题
协同过滤是推荐系统中广泛使用的技术,其核心思想是利用用户行为数据发现相似用户或物品进行推荐。摘要包括:1)协同过滤基于用户历史行为数据,通过计算相似度(如余弦相似度、皮尔逊相关系数)预测用户偏好;2)主要分为基于用户(寻找相似用户群体)和基于物品(发现相似物品)两种方法;3)面临冷启动、数据稀疏性等挑战,可通过混合推荐(结合内容特征)和矩阵分解等技术解决;4)典型应用包括电商猜你喜欢和流媒体推荐;5)结合大语言模型可增强语义理解能力,提升推荐准确性。
267 9
|
27天前
|
机器学习/深度学习 人工智能 自然语言处理
AgentEvolver:让智能体系统学会「自我进化」
AgentEvolver 是一个自进化智能体系统,通过自我任务生成、经验导航与反思归因三大机制,推动AI从“被动执行”迈向“主动学习”。它显著提升强化学习效率,在更少参数下实现更强性能,助力智能体持续自我迭代。开源地址:https://github.com/modelscope/AgentEvolver
743 38
|
24天前
|
人工智能 数据挖掘 关系型数据库
内附原文|VLDB论文精读:AI进行时,数据分析迈入增量计算时代
阿里云AnalyticDB团队近期在VLDB 2025上发表了关于增量计算的最新研究成果——论文《Streaming View: An Efficient Data Processing Engine for Modern Real-time Data Warehouse of Alibaba Cloud》。本文将对该工作进行简要介绍。
|
29天前
|
SQL Oracle 关系型数据库
【赵渝强老师】Oracle数据库的PL/SQL编程语言
PL/SQL是Oracle对SQL的扩展,结合过程化编程特性,支持开发存储过程、函数和触发器。本文介绍其基本结构,并演示如何在SQL*Plus中编写“Hello World”程序,需启用serveroutput输出。也可使用Oracle SQL Developer更高效地开发调试PL/SQL应用。
|
7月前
|
分布式计算 Java Scala
【赵渝强老师】Scala编程语言
Scala 是一种集成面向对象与函数式编程特性的多范式语言,运行于 Java 平台并兼容 Java 程序。学习 Scala 为掌握 Spark 和 Flink 打下基础。本文通过视频讲解及代码示例,展示如何用 Scala 在 Spark 和 Flink 中实现 WordCount 程序,包括环境配置、数据处理及输出操作,帮助理解其实际应用。
148 19
|
8月前
|
存储 SQL 缓存
Apache Doris & SelectDB 技术能力全面解析
本文将对 Doris & SelectDB 适合的分析场景和技术能力进行概述解析
1361 1
Apache Doris & SelectDB 技术能力全面解析
|
8月前
|
存储 Cloud Native 关系型数据库
【赵渝强老师】TiDB的功能特性
TiDB是由PingCAP公司自主研发的开源分布式关系型数据库,支持HTAP(混合事务与分析处理),具备一键水平扩缩容、金融级高可用、实时HTAP、云原生架构及兼容MySQL协议等核心功能。其分布式设计可满足高可用、强一致性和大规模数据处理需求,适用于多种应用场景。视频讲解与详细功能介绍进一步展示了其技术优势和生态兼容性。
249 6
|
11月前
|
存储 运维 监控
金融场景 PB 级大规模日志平台:中信银行信用卡中心从 Elasticsearch 到 Apache Doris 的先进实践
中信银行信用卡中心每日新增日志数据 140 亿条(80TB),全量归档日志量超 40PB,早期基于 Elasticsearch 构建的日志云平台,面临存储成本高、实时写入性能差、文本检索慢以及日志分析能力不足等问题。因此使用 Apache Doris 替换 Elasticsearch,实现资源投入降低 50%、查询速度提升 2~4 倍,同时显著提高了运维效率。
635 3
金融场景 PB 级大规模日志平台:中信银行信用卡中心从 Elasticsearch 到 Apache Doris 的先进实践
|
9月前
|
SQL Oracle 关系型数据库
【赵渝强老师】Oracle数据库的客户端工具
本文介绍了Oracle数据库的三种客户端工具:SQL*Plus、Oracle Enterprise Manager Database Express(EM)和SQL Developer的使用方法。首先通过命令行工具SQL*Plus登录数据库,创建用户并授权,建立部门与员工表,插入数据并查询;接着讲解了如何通过浏览器访问EM界面监控数据库及表空间状态;最后演示了SQL Developer的下载安装、连接配置以及执行查询的过程,帮助用户快速上手Oracle数据库管理与操作。
188 0