首页 > 数据库 >PostgreSQL锁,第1部分:行锁

PostgreSQL锁,第1部分:行锁

时间:2023-11-25 19:46:41浏览次数:49  
标签:PostgreSQL 行锁 pid pg xmax 部分 id SELECT

理解PostgreSQL锁对于构建可伸缩的应用程序和避免停机是很重要的。现代计算机和服务器有许多CPU核心,可以并行执行多个查询。数据库包含许多一致的结构,这些结构由并行运行的查询或后台进程所做的更改可能导致数据库崩溃,甚至损坏数据。因此,我们需要能够阻止来自并发进程的访问,同时更改共享内存结构或行。一个线程更新结构,而其他线程等待(独占锁),或者多个线程读取结构,所有写入等待。等待的副作用是锁定争用和服务器资源浪费。因此,理解为什么会发生等待以及涉及哪些锁是很重要的。在本文中,我将回顾PostgreSQL行级锁。

目录

在后续的文章中,我将研究表级锁锁存器保护内部数据库结构。

行锁-概述

PostgreSQL在不同的抽象级别上有许多锁。应用程序中最重要的锁与MVCC实现行级锁相关。其次——在维护任务期间(在备份/数据库迁移模式更改期间)出现的锁——表级锁。在低级PostgreSQL锁上也有可能看到等待,但这种情况很少见。更常见的情况是,CPU使用率很高,有许多并发查询正在运行,但与正常的并行查询数量相比,整体服务器性能下降。

示例环境

接下来,你需要一个PostgreSQL服务器,它有一个单列表,包含几行:

postgres=# CREATE TABLE locktest (c INT);
CREATE TABLE
postgres=# INSERT INTO locktest VALUES (1), (2);
INSERT 0 2

行锁

场景:两个并发事务试图选择要更新的一行。

PostgreSQL在这种情况下使用行级锁。行级锁定与MVCC实现紧密集成,并使用隐藏的xminxmax字段。xminxmax存储事务id。所有需要行级锁的语句都会修改xmax字段(甚至包括SELECT FOR UPDATE)。修改发生在查询返回结果之后,所以为了看到xmax的变化,我们需要运行两次SELECT FOR UPDATE。通常,xmax字段用于将一行标记为过期(要么被某些事务完全删除,要么用于更新的行版本),但它也用于行级锁定基础结构。

postgres=# BEGIN;
BEGIN
postgres=# SELECT xmin,xmax, txid_current(), c FROM locktest WHERE c=1 FOR UPDATE;
   xmin   | xmax | txid_current | c
----------+------+--------------+---
 12594472 |    0 |     12594473 | 1
(1 row)

postgres=# SELECT xmin,xmax, txid_current(), c FROM locktest WHERE c=1 FOR UPDATE;
   xmin   |   xmax   | txid_current | c
----------+----------+--------------+---
 12594472 | 12594473 |     12594473 | 1
(1 row)

如果语句试图修改同一行,则检查未完成事务列表。语句必须等待修改,直到id=xmax的事务完成。

没有用于等待特定行的基础设施,但是事务可以等待事务id。

-- second connection
SELECT xmin,xmax,txid_current() FROM locktest WHERE c=1 FOR UPDATE;

在第二个连接中运行的SELECT FOR UPDATE查询未完成,正在等待第一个事务完成。

pg_locks

这样的等待和锁可以通过查询pg_locks来查看:

postgres=# SELECT locktype,transactionid,virtualtransaction,pid,mode,granted,fastpath FROM pg_locks WHERE transactionid=12594473;
   locktype    | transactionid | virtualtransaction |  pid  |     mode      | granted | fastpath
---------------+---------------+--------------------+-------+---------------+---------+----------
 transactionid |      12594473 | 5/48               | 33358 | ShareLock     | f       | f
 transactionid |      12594473 | 4/13981            | 33325 | ExclusiveLock | t       | f
(2 rows)

您可以看到locktype=transactionid == 12594473的写入器事务id。让我们获取持有锁的pid和后端id:

postgres=# SELECT id,pg_backend_pid() FROM pg_stat_get_backend_idset() AS t(id) WHERE pg_stat_get_backend_pid(id) = pg_backend_pid();
 id | pg_backend_pid
----+----------------
  5 |          33418
(1 row)

每个后端都有一个OS进程标识符(PID)和内部PostgreSQL标识符(backend id)。PostgreSQL可以处理许多事务,但是锁定只能发生在后端之间,并且每个后端执行一个事务。内部簿记只需要一个虚拟事务标识符:后端内部的一对后端id和一个序列号。

不管被锁的行数是多少,PostgreSQL在pg_locks表中只有一个相关的锁。查询可能修改数十亿行,但PostgreSQL不会为冗余锁结构浪费内存。

写入线程在其transactionid上设置ExclusiveLock。所有行级锁等待器都设置了ShareLock。一旦写入器释放锁,锁管理器将恢复之前锁定的所有后端锁。

transactionid的锁释放发生在提交或回滚时。

pg_stat_activity

另一个获取锁定相关细节的好方法是从pg_stat_activity表中选择:

postgres=# SELECT pid,backend_xid,wait_event_type,wait_event,state,query FROM pg_stat_activity WHERE pid IN (33358, 33325);
-[ RECORD 1 ]---+------------------------------------------------------------------------
pid             | 33325
backend_xid     | 12594473
wait_event_type |
wait_event      |
state           | idle in transaction
query           | SELECT xmin,xmax, txid_current(), c FROM locktest WHERE c=1 FOR UPDATE;
-[ RECORD 2 ]---+------------------------------------------------------------------------
pid             | 33358
backend_xid     | 12594474
wait_event_type | Lock
wait_event      | transactionid
state           | active
query           | SELECT xmin,xmax,txid_current() FROM locktest WHERE c=1 FOR UPDATE;

源代码级调查

让我们用gdb和pt-pmp工具检查服务员的堆栈跟踪:

# pt-pmp -p 33325
Sat Jul 28 10:10:25 UTC 2018
30	../sysdeps/unix/sysv/linux/epoll_wait.c: No such file or directory.
1 epoll_wait,WaitEventSetWaitBlock,WaitEventSetWait,WaitLatchOrSocket,WaitLatch,ProcSleep,WaitOnLock,LockAcquireExtended,LockAcquire,XactLockTableWait,heap_lock_tuple,ExecLockRows,ExecProcNode,ExecutePlan,standard_ExecutorRun,PortalRunSelect,PortalRun,exec_simple_query,PostgresMain,BackendRun,BackendStartup,ServerLoop,PostmasterMain,main

WaitOnLock函数导致等待。函数位于lock.c文件(POSTGRES主锁机制)中。

锁表是一个共享内存哈希表。冲突进程在存储区/lmgr/proc.c中休眠。在大多数情况下,应该通过lmgr.c或其他锁管理模块调用此代码,而不是直接调用。

接下来,在pg_stat_activity中作为“Lock”列出的锁也称为重量级锁,并由锁管理器控制。hwlock也用于许多高级操作。

顺便说一下,完整的描述可以在这里找到:https://www.postgresql.org/docs/current/static/explicit-locking.html

总结

  • 避免长时间运行的事务修改频繁更新的行或修改过多的行
  • 其次,不要在MVCC数据库中使用热点(由多个应用程序客户端连接并行更新的单行或多行)。这种工作负载更适合于内存中的数据库,并且通常可以与主要业务逻辑分离。

标签:PostgreSQL,行锁,pid,pg,xmax,部分,id,SELECT
From: https://www.cnblogs.com/jl1771/p/17855945.html

相关文章

  • PostgreSQL锁,第2部分:重量级锁
    应用程序开发人员和dba的PostgreSQL锁可见性在大多数情况下与重量级锁相关。复杂的数据库锁定操作需要使用来自系统目录的视图进行完整的检测。应该清楚哪个对象被特定的数据库“后端”进程锁定。任何锁的另一个名称是“瓶颈”。为了使数据库操作并行,我们应该将单个瓶颈分解为多个......
  • 为PostgreSQL优化调整Linux内核参数
    为了获得最佳性能,PostgreSQL数据库依赖于正确定义的操作系统参数。配置不当的操作系统内核参数可能导致数据库服务器性能下降。因此,必须根据数据库服务器及其工作负载来配置这些参数。在这篇文章中,我们将讨论一些可能影响数据库服务器性能的重要Linux内核参数,以及如何调优这些参数......
  • PostgreSQL中的复制延迟
    PostgreSQL是一种流行的开源关系数据库管理系统,广泛用于存储和管理数据。PostgreSQL中可能遇到的常见问题之一是复制滞后。在这篇博客中,我们将讨论什么是复制延迟、它发生的原因以及如何在PostgreSQL中缓解它。什么是复制滞后?复制延迟是指数据写入主数据库的时间与复制到备......
  • cryptohack RSA部分
    1.Salty:此题为rsa加密中e=1的情况,由于\(ed(modphi)=1\),所以d自然是等于1的,不用分解n就解出了dfromCrypto.Util.numberimportlong_to_bytese=1k=1ct=44981230718212183604274785925793145442655465025264554046028251311164494127485n=110581795715958566206600392161360......
  • Ubuntu20.04 安装后部分问题解决方案
    安装搜狗输入法搜狗官方有教程:https://shurufa.sogou.com/linux/guideUbuntu与Windows时间不一致的问题安装ntpdate:sudoapt-getinstallntpdate校准时间:sudontpdatetime.windows.com将时间更新到硬件上:sudohwclock--localtime--systohc单击任务栏图标使窗......
  • postgresql从入门到精通 - 第35讲:中间件PgBouncer部署|PostgreSQL教程
     PostgreSQL从小白到专家,是从入门逐渐能力提升的一个系列教程,内容包括对PG基础的认知、包括安装使用、包括角色权限、包括维护管理、、等内容,希望对热爱PG、学习PG的同学们有帮助,欢迎持续关注CUUGPG技术大讲堂。 第35讲:中间件PgBouncer部署11月25日(周六)19:30-20:30,往期......
  • 印度程序员指针部分部分代码
    #include"stdio.h"intmain(){ intx=5; int*p=&x; *p=6;//可以不改变x的值来修改输出 int*(*q)=&p;//即p=*q int*(*(*r))=&q;//即r=*p printf("%d\n",*p); printf("%d\n",*q); printf("%d\n",**q);//即*p pr......
  • 直播平台源码,vue图片中划框截取部分图片
    直播平台源码,vue图片中划框截取部分图片 <template> <div>  <el-dialogtitle="请在图片上画出需要新增故障的位置":visible.sync="dialogVisible"width="1270px":before-close="handleClose":close-on-click-modal="false":close......
  • JWT生成的token——中间部分Payload的坑
     JWT进行token认证应该都用过,标准的加密(Base64编码)后的token是这样的三段式的:eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWQiOiIxMjM0NTY3ODkwIiwiZXhwIjoxNjA1MDAxNzQyLCJpYXQiOjE2MDQ5OTQ1NDIsImp0aSI6IjU5YjI2NDEzLTE4MjMtNDVlZS1iZTI1LTA5M2ZjMjlhMmYzOCJ9.FMpVju......
  • eBPF 概述:第 2 部分:机器和字节码
    1.前言我们在第1篇文章中介绍了eBPF虚拟机,包括其有意的设计限制以及如何从用户空间进程中进行交互。如果你还没有读过这篇文章,建议你在继续之前读一下,因为没有适当的介绍,直接开始接触机器和字节码的细节是比较困难的。如果有疑问,请看第1部分开头的流程图。本系列的第2......