PostgreSQL 锁等待排查

说明

在数据库中,常用 MVCC 来保障事务的一致性及提高并发性。锁问题的定位和排查也是数据库运维人员必会的技能,本篇文章介绍 PostgreSQL 如何排查定位锁堵塞问题。

1. PostgreSQL 的锁

这里推荐阅读官方文档,关于 PostgreSQL 锁模式介绍。

2. 死锁问题

如果业务逻辑设计不慎,可能导致严重的锁等待,或者死锁的产生。PostgreSQL 自动检测死锁情况并会自动回滚其中一个事务进行处理,从而其他事务提交。请看下方 Case:

postgres= select * from pgbench_accounts where aid in (3, 4, 5);
 aid | bid | abalance |                                        filler                                        
-----+-----+----------+--------------------------------------------------------------------------------------
   3 |   1 |        0 |                                                                                     
   4 |   1 |        0 |                                                                                     
   5 |   1 |        0 |                                                                                     
(3 rows)
事务一 事务二
begin; begin;
update pgbench_accounts set abalance = 1 where aid = 3; update pgbench_accounts set abalance = 1 where aid = 4;
- update pgbench_accounts set abalance = 1 where aid = 3; – 被堵塞
update pgbench_accounts set abalance = 1 where aid = 4; update 执行成功
commit – 自动 rollback commit;

ERROR: deadlock detected
DETAIL: Process 127589 waits for ShareLock on transaction 702; blocked by process 127570.
Process 127570 waits for ShareLock on transaction 701; blocked by process 127589.
HINT: See server log for query details.
CONTEXT: while updating tuple (0,4) in relation “pgbench_accounts”

PostgreSQL 有自动死锁检测机制,和 MySQL 相同检查到死锁后,如果大于锁超时时间,会自动回滚其中一个事务,默认为 1s:

select current_setting('deadlock_timeout');

MySQL 可以通过 show engine innodb status 获得历史死锁信息,关于 PostgreSQL 可以通过参数控制log_lock_waits 参数将死锁信息输出到日志中:

2023-07-03 16:39:26.037 CST [127589] LOG: process 127589 detected deadlock while waiting for ShareLock on transaction 704 after 1000.332 ms
2023-07-03 16:39:26.037 CST [127589] DETAIL: Process holding the lock: 14138. Wait queue: .
2023-07-03 16:39:26.037 CST [127589] CONTEXT: while updating tuple (0,4) in relation “pgbench_accounts”
2023-07-03 16:39:26.037 CST [127589] STATEMENT: update pgbench_accounts set abalance = 1 where aid = 4;
2023-07-03 16:39:26.037 CST [127589] ERROR: deadlock detected
2023-07-03 16:39:26.037 CST [127589] DETAIL: Process 127589 waits for ShareLock on transaction 704; blocked by process 14138.
Process 14138 waits for ShareLock on transaction 703; blocked by process 127589.
Process 127589: update pgbench_accounts set abalance = 1 where aid = 4;
Process 14138: update pgbench_accounts set abalance = 1 where aid = 3;
2023-07-03 16:39:26.037 CST [127589] HINT: See server log for query details.
2023-07-03 16:39:26.037 CST [127589] CONTEXT: while updating tuple (0,4) in relation “pgbench_accounts”
2023-07-03 16:39:26.037 CST [127589] STATEMENT: update pgbench_accounts set abalance = 1 where aid = 4;
2023-07-03 16:39:26.038 CST [14138] LOG: process 14138 acquired ShareLock on transaction 703 after 25470.277 ms
2023-07-03 16:39:26.038 CST [14138] CONTEXT: while updating tuple (0,3) in relation “pgbench_accounts”
2023-07-03 16:39:26.038 CST [14138] STATEMENT: update pgbench_accounts set abalance = 1 where aid = 3;

3. 锁问题监控

3.1 pg_stat_activity

该视图是来观测数据库会话的,和 MySQL 中的 processlist 作用相同,也可以通过它观测锁等待:

select pid, 
	   pg_blocking_pids(pid),
	   wait_event_type,wait_event,
	   query 
from pg_stat_activity;
Session 1 Session 2
Begin;
delete from pgbench_accounts where aid = 3;
select * from pgbench_accounts where aid = 3 for update;

此时 Session 2 被 Session 1 blocking 使用 pg_stat_activity 查询下:

  pid   | pg_blocking_pids | wait_event_type |     wait_event      |                          query                           
--------+------------------+-----------------+---------------------+----------------------------------------------------------
 127589 | {}               | Client          | ClientRead          | delete from pgbench_accounts where aid = 3;
  14138 | {127589}         | Lock            | transactionid       | select * from pgbench_accounts where aid = 3 for update;
 129038 | {}               |                 |                     | select pid,+
        |                  |                 |                     |    pg_blocking_pids(pid),+
        |                  |                 |                     |    wait_event_type,wait_event,+
        |                  |                 |                     |    query                                                +
        |                  |                 |                     | from pg_stat_activity;

可以看清楚的看到 select…for update 语句被 pid = 127589 的会话堵塞。

3.2 堵塞视图

因为这条 SQL 非常长,使用前建议创建视图,方便后续使用:

create view v_locks_monitor as   
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,   
  b.state,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,relation,classid,objsubid ;

还是 2.1 小节,人为制造的锁等待场景,我们使用该视图看看效果:

select * from v_locks_monitor;
-[ RECORD 1 ]-+------------------------------------------------------------------------------------------------------------------------------------------------------
locktype      | transactionid
datname       | postgres
relation      | 
page          | 
tuple         | 
virtualxid    | 
transactionid | 705
classid       | 
objid         | 
objsubid      | 
lock_conflict | Pid: 127589                                                                                                                                          +
              | Lock_Granted: true , Mode: ExclusiveLock , FastPath: false , VirtualTransaction: 3/382737 , Session_State: idle in transaction                       +
              | Username: postgres , Database: postgres , Client_Addr: NULL , Client_Port: -1 , Application_Name: psql                                               +
              | Xact_Start: 2023-07-03 16:51:06.570461+08 , Query_Start: 2023-07-03 16:51:27.61686+08 , Xact_Elapse: 00:13:38.834827 , Query_Elapse: 00:13:17.788428 +
              | SQL (Current SQL in Transaction):                                                                                                                    +
              | delete from pgbench_accounts where aid = 3;                                                                                                          +
              | --------                                                                                                                                             +
              | Pid: 14138                                                                                                                                           +
              | Lock_Granted: false , Mode: ShareLock , VirtualTransaction: 4/43636 , Session_State: active                                        +
              | Username: postgres , Application_Name: psql                                               +
              | Xact_Start: 2023-07-03 16:56:05.622813+08 , Query_Start: 2023-07-03 16:56:05.622813+08 , Xact_Elapse: 00:08:39.782475 , Query_Elapse: 00:08:39.782475+
              | SQL (Current SQL in Transaction):                                                                                                                    +
              | select * from pgbench_accounts where aid = 3 for update;

此视图会按锁的大小排序,要快速解除锁堵塞状态,terminate 最大的锁对应的 PID 即可。

select pg_terminate_backend(127589);

4. 锁相关参数

4.1 deadlock_timeout

默认1s,表示 pg 数据库仅对锁超时大于 1s 的情况进行死锁检测。

4.2 log_lock_waits

默认关闭,若打开该参数则表示会将锁超时超过 deadlock_timeout 的信息记录到日志中。

4.3 lock_timeout

锁等待超时,默认为 0 表示禁用锁超时。若该参数设置 >0 则表示会话等待锁资源 Ns 后如果仍无法获取到相关锁资源则终止相关语句执行。

4.4 idle_in_transaction_session_timeout

对于一个空闲的事务,超过多少时间后自动终止,单位为毫秒。默认为 0 表示禁用该参数,会话终止会释放该会话所有锁资源。

后记

Reference:

原文地址:https://blog.csdn.net/qq_42768234/article/details/131516728

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


文章浏览阅读601次。Oracle的数据导入导出是一项基本的技能,但是对于懂数据库却不熟悉Oracle的同学可能会有一定的障碍。正好在最近的一个项目中碰到了这样一个任务,于是研究了一下Oracle的数据导入导出,在这里跟大家分享一下。......_oracle 迁移方法 对比
文章浏览阅读553次。开头还是介绍一下群,如果感兴趣polardb ,mongodb ,mysql ,postgresql ,redis 等有问题,有需求都可以加群群内有各大数据库行业大咖,CTO,可以解决你的问题。加群请联系 liuaustin3 ,在新加的朋友会分到2群(共700多人左右 1 + 2)。最近我们在使用MYSQL 8 的情况下(8.025)在数据库运行中出现一个问题 参数prefer_order_i..._mysql prefer_ordering_index
文章浏览阅读3.5k次,点赞3次,收藏7次。折腾了两个小时多才成功连上,在这分享一下我的经验,也仅仅是经验分享,有不足的地方欢迎大家在评论区补充交流。_navicat连接opengauss
文章浏览阅读2.7k次。JSON 代表 JavaScript Object Notation。它是一种开放标准格式,将数据组织成中详述的键/值对和数组。_postgresql json
文章浏览阅读2.9k次,点赞2次,收藏6次。navicat 连接postgresql 注:navicat老版本可能报错。1.在springboot中引入我们需要的依赖以及相应版本。用代码生成器生成代码后,即可进行增删改查(略)安装好postgresql 略。更改配置信息(注释中有)_mybatisplus postgresql
文章浏览阅读1.4k次。postgre进阶sql,包含分组排序、JSON解析、修改、删除、更新、强制踢出数据库所有使用用户、连表更新与删除、获取今年第一天、获取近12个月的年月、锁表处理、系统表使用(查询所有表和字段及注释、查询表占用空间)、指定数据库查找模式search_path、postgre备份及还原_pgsql分组取每组第一条
文章浏览阅读3.3k次。上一篇我们学习了日志清理,日志清理虽然解决了日志膨胀的问题,但就无法再恢复检查点之前的一致性状态。因此,我们还需要日志归档,pg的日志归档原理和Oracle类似,不过归档命令需要自己配置。以下代码在postmaster.c除了开启归档外,还需要保证wal_level不能是MINIMAL状态(因为该状态下有些操作不会记录日志)。在db启动时,会同时检查archive_mode和wal_level。以下代码也在postmaster.c(PostmasterMain函数)。......_postgresql archive_mode
文章浏览阅读3k次。系统:ubuntu22.04.3目的:利用向日葵实现windows远程控制ubuntu。_csdn局域网桌面控制ubuntu
文章浏览阅读1.6k次。表分区是解决一些因单表过大引用的性能问题的方式,比如某张表过大就会造成查询变慢,可能分区是一种解决方案。一般建议当单表大小超过内存就可以考虑表分区了。1,继承式分区,分为触发器(trigger)和规则(rule)两种方式触发器的方式1)创建表CREATE TABLE "public"."track_info_trigger_partition" ( "id" serial, "object_type" int2 NOT NULL DEFAULT 0, "object_name..._pg数据表分区的实现
文章浏览阅读3.3k次。物联网平台开源的有几个,就我晓得的有、、thingskit、JetLink、DG-iot(还有其他开源的,欢迎在评论区留言哦!),然后重点分析了下ThingsBoard、ThingsPanel和JetLink,ThingsBoard和Jetlinks是工程师思维产品,可以更多的通过配置去实现开发的目的,ThingsPanel是业务人员思路产品,或者开发或者用,避免了复杂的配置带来的较高学习门槛。ThingsBoard和Jetlinks是Java技术体系的,ThingsPanel是PHP开发的。_jetlinks和thingsboard
文章浏览阅读3.8k次。PostgreSQL 数据类型转换_pgsql数字转字符串
文章浏览阅读7k次,点赞3次,收藏14次。在做数据统计页面时,总会遇到统计某段时间内,每天、每月、每年的数据视图(柱状图、折线图等)。这些统计数据一眼看过去也简单呀,不就是按照时间周期(天、月、年)对统计数据进行分个组就完了嘛?但是会有一个问题,简单的写个sql对周期分组,获取到的统计数据是缺失的,即没有数据的那天,整条记录也都没有了。如下图需求:以当前月份(2023年2月)为起点,往后倒推一年,查询之前一年里每个月的统计数据。可见图中的数据其实是缺少的,这条sql只查询到了有数据的月份(23年的1月、2月,22年的12月)_如何用一条sql查出按年按月按天的汇总
文章浏览阅读3.8k次,点赞66次,收藏51次。PostgreSQL全球开发小组与2022年10月13日,宣布发布PostgreSQL15,这是世界上最先进的开源数据库的最新版本_mysql8 postgresql15
文章浏览阅读1.3k次。上文介绍了磁盘管理器中VFD的实现原理,本篇将从上层角度讲解磁盘管理器的工作细节。_smgrrelationdata
文章浏览阅读1.1k次。PostgreSQL设置中文语言界面和局域网访问_postgressql汉化
文章浏览阅读4.2k次。PostgreSQL 修改数据存储路径_如何设置postgresql 数据目录
文章浏览阅读4.7k次。在项目中用到了多数据源,在连接postgres数据库时,项目启动报错,说数据库连接错误,说dual不存在,网上好多教程都是说数据库查询的时候的大小写问题,而这个仅仅是连接,咋鞥却处理方法是修改application-dev.yml中的配置文件.项目中的druid参数是这样的:确实在配置文件中有个查询语句。_relation "dual" does not exist
文章浏览阅读4.9k次。PostgreSQL是一款强大的关系型数据库,但在实际使用过程中,许多用户经常会遇到慢SQL的问题。这些问题不仅会降低数据库性能,还会直接影响业务流程和用户体验。因此,本文将会深入分析PostgreSQL慢SQL的原因和优化方案,帮助用户更好地利用这个优秀的数据库系统。无论你是初学者还是专业开发者,本文都将为你提供实用的技巧和方法,让你的PostgreSQL数据库始终保持高效快速。_postgresql数据库优化
文章浏览阅读1.6k次。Linux配置postgresql开机自启_linux 启动pgsql
文章浏览阅读2k次。本篇介绍如何在centos7系统搭建一个postgresql主备集群实现最近的HA(高可用)架构。后续更高级的HA模式都是基于这个最基本的主备搭建。_postgresql主备