一、 四种行锁

1. 简介与兼容性分析

pg采用元组级常规锁+xmax结合的方式实现行锁。我们曾经提到过常规锁是有很多类TAG的(typedef enum LockTagType),其中 LOCKTAG_TUPLE就是用来对元组加锁的。

不单纯用元组级常规锁,是为了避免事务修改行过多时,锁表急剧增大导致性能劣化,并且锁表在共享内存中的大小是有限的。因此,行锁是不存储在内存中的。

pg中通常有两种方式会用到行锁:

  • 对行执行update,delete操作
  • 显式指定行锁(例如select for update)

在新版本中,显式加行锁共有4种写法

行锁类型

简介

FOR UPDATE

FOR UPDATE会锁定SELECT检索到的行,就好像它们要被更新。

这可以阻止它们被其他事务锁定、修改或者删除,直到当前事务结束。

DELETE和修改主键、唯一键值的UPDATE会获得这种锁模式。

FOR NO KEY UPDATE

行为与FOR UPDATE类似,不过获得的锁较弱,不会阻塞SELECT FOR KEY SHARE。

不修改主键、唯一键值的UPDATE会获得这种锁模式。

FOR SHARE

行为与FOR NO KEY UPDATE类似,不过它在每个检索到的行上获得一个共享锁而不是排他锁。

读该行,不允许对行进行更新

FOR KEY SHARE

行为与FOR SHARE类似,不过锁更弱,不会被SELECT
FOR NO KEY UPDATE阻塞。

读该行键值,但允许对除键外的其他字段更新。在外键检查时使用该锁

4种行锁兼容性如下

详细

4种行锁兼容性如下

详细可参考  官方文档第13章 13.3.2. Row-Level Locks

2. 对应源码

它们对应的源码在lockoptions.h文件

/** Possible lock modes for a tuple.*/
typedef enum LockTupleMode
{/* SELECT FOR KEY SHARE */LockTupleKeyShare,/* SELECT FOR SHARE */LockTupleShare,/* SELECT FOR NO KEY UPDATE, and UPDATEs that don't modify key columns */LockTupleNoKeyExclusive,/* SELECT FOR UPDATE, UPDATEs that modify key columns, and DELETE */LockTupleExclusive
} LockTupleMode;

如前所述,pg行锁是由常规锁+xmax结合实现的,因此它需要建立常规锁与LockTupleMode之间的映射关系。

/** Each tuple lock mode has a corresponding heavyweight lock, and one or two* corresponding MultiXactStatuses (one to merely lock tuples, another one to* update them).  This table (and the macros below) helps us determine the* heavyweight lock mode and MultiXactStatus values to use for any particular* tuple lock strength.** Don't look at lockstatus/updstatus directly!  Use get_mxact_status_for_lock* instead.*/
static const struct
{LOCKMODE   hwlock;  // 对应的常规锁模式int         lockstatus;  // 显式指定的锁模式int         updstatus;   // 隐式指定的锁模式
}tupleLockExtraInfo[MaxLockTupleMode + 1] =
{{                          /* LockTupleKeyShare */AccessShareLock,          // 1级表锁MultiXactStatusForKeyShare,-1                       /* KeyShare does not allow updating tuples */},{                            /* LockTupleShare */RowShareLock,             // 2级表锁MultiXactStatusForShare,-1                     /* Share does not allow updating tuples */},{                           /* LockTupleNoKeyExclusive */ExclusiveLock,             // 7级表锁MultiXactStatusForNoKeyUpdate,MultiXactStatusNoKeyUpdate},{                          /* LockTupleExclusive */AccessExclusiveLock,       // 8级表锁MultiXactStatusForUpdate,MultiXactStatusUpdate}
};

其实这些行锁模式的兼容性是怎么来的,就是根据对应的表锁兼容性来的。例如一个delete操作,它在表上加的是3级锁,但在行上加的是8级锁。因此在表上该操作是兼容的(可以同时对一个表进行delete),但在行上是冲突的(不可以同时delete同一行)。

二、 xmax的设置

通过前面的源码可以看到,tupleLockExtraInfo中除了保存常规锁对应的锁模式,还保存了大量MultiXact中的锁模式,MultiXact又是个什么呢?

1. MultiXactId

通常如果只有一个事务增加行锁,那么直接将行的xmax设为事务id,并在infomask中设置对应锁类型即可。

但select…for…语句中可以加行级共享锁,即可以有多个事务对一个元组加共享锁,这时就没法通过将行的xmax设为事务id来表示了。为此,pg将多个事务组成一个mXactCacheEnt(multixact.c文件),并为其指定唯一的MultiXactId,此时在xmax处保存的就是MultiXactId。

typedef struct mXactCacheEnt
{MultiXactId multi;int          nmembers;dlist_node node;MultiXactMember members[FLEXIBLE_ARRAY_MEMBER];
} mXactCacheEnt;

为了区分xmax设置的是事务id还是MultiXactId,在使用MultiXactId时会在元组上增加HEAP_XMAX_IS_MULTI标记。

2. 模拟案例

会话1

create table t1(a int primary key,b int);
insert into t1 values(1,1);
select xmin,xmax,* from t1; -- 事务id 761

会话2

Begin;
select * from t1 for key share; -- 事务不提交

会话1

select xmin,xmax,* from t1; -- 当前xmax中保存的是事务id(762)

会话3

Begin;
select * from t1 for share; -- 事务不提交(事务id 763)

会话1

select xmin,xmax,* from t1; -- 当前xmax中保存的是MultiXactId

怎么知道这个是MultiXactId?可以通过控制文件查看

pg_controldata –D $PGDATA | grep Multi

3. 行锁标记位

  • 如果元组的xmax是事务id,需要通过infomask标记位区分元组的加锁情况。

源代码在htup_details.h,其实有很多种状态,这里只简单列一些

#define HEAP_XMAX_KEYSHR_LOCK    0x0010  /* for key share子句对应的锁 */
#define HEAP_XMAX_EXCL_LOCK     0x0040  /* xmax is exclusive locker,排他锁标记位 */
/* xmax is a shared locker */
#define HEAP_XMAX_SHR_LOCK  (HEAP_XMAX_EXCL_LOCK | HEAP_XMAX_KEYSHR_LOCK)
#define HEAP_XMAX_LOCK_ONLY     0x0080  /* xmax, if valid, is only a locker,显式加行锁 */
#define HEAP_XMAX_IS_MULTI      0x1000  /* t_xmax is a MultiXactId */
#define HEAP_KEYS_UPDATED       0x2000  /* tuple was updated and key cols */

详细可参考  https://mp.weixin.qq.com/s/nDBYbJpoBVqZxpYYaxPZ6Q

  • 如果元组的xmax是MultiXactId,则每种子句都对应一种锁模式(它们的对应关系通过tupleLockExtraInfo也可以看出来)
/** Possible multixact lock modes ("status").  The first four modes are for* tuple locks (FOR KEY SHARE, FOR SHARE, FOR NO KEY UPDATE, FOR UPDATE); the* next two are used for update and delete modes.*/
typedef enum
{MultiXactStatusForKeyShare = 0x00,MultiXactStatusForShare = 0x01,MultiXactStatusForNoKeyUpdate = 0x02,MultiXactStatusForUpdate = 0x03,/* an update that doesn't touch "key" columns */MultiXactStatusNoKeyUpdate = 0x04,/* other updates, and delete */MultiXactStatusUpdate = 0x05
} MultiXactStatus;

为了保存MultiXactId和事务的映射关系,pg使用两个SLRU进行分层映射,它们位于$PGDATA/pg_multixact目录下,分别是offsets目录和members目录。

参考

《PostgreSQL技术内幕:事务处理深度探索》第2章

PostgreSQL锁机制——行级锁 - JavaShuo

https://www.modb.pro/db/70021

https://www.modb.pro/video/5128?sjhy

postgresql源码学习(十三)—— 行锁①-行锁模式与xmax相关推荐

  1. postgresql源码学习(九)—— 常规锁②-强弱锁与Fast Path

    一. 强锁与弱锁 根据兼容性表,彼此相容的3个锁(1-3级,AccessShareLock.RowShareLock.RowExclusiveLock)是弱锁,4级锁ShareUpdateExclus ...

  2. postgresql源码学习(51)—— 提交日志CLOG 原理 用途 管理函数

    一. CLOG是什么 CLOG(commit log)记录事务的最终状态. 物理上,是$PGDATA/pg_xact目录下的一些文件 逻辑上,是一个数组,下标为事务id,值为事务最终状态 1. 事务最 ...

  3. postgresql源码学习(27)—— 事务日志⑦-日志落盘上层函数 XLogFlush

    一. 预备知识 1. XLOG什么时候需要落盘 事务commit之前 log buffer被覆盖之前 后台进程定期落盘 2. 两个核心结构体 这两个结构体定义代码在xlog.c,它们在日志落盘过程中非 ...

  4. PostgreSQL源码学习(1)--PG13代码结构

    PostgreSQL源码学习(1)–PG13代码结构 PostgreSQL代码结构 Bootstrap:用于支持Bootstrap运行模式,该模式主要用来创建初始的模板数据库. Main:主程序模块, ...

  5. PostgreSQL源码学习(一)编译安装与GDB入门

    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 PostgreSQL源码学习(一)编译安装与GDB入门 前言 一.安装PostgreSQL 1.获取源码 2.配置 3.编译 3.安装 ...

  6. postgresql源码学习(49)—— MVCC⑤-cmin与cmax 同事务内的可见性判断

    一. 难以理解的场景 postgresql源码学习(十九)-- MVCC④-可见性判断 HeapTupleSatisfiesMVCC函数_Hehuyi_In的博客-CSDN博客 在前篇的可见性判断中有 ...

  7. postgresql源码学习(53)—— vacuum②-lazy vacuum之heap_vacuum_rel函数

    一. table_relation_vacuum函数 1. 函数定义 前篇最后(https://blog.csdn.net/Hehuyi_In/article/details/128749517),我 ...

  8. postgresql源码学习(52)—— vacuum①-准备工作与主要流程

    关于vacuum的基础知识,参考,本篇从源码层继续学习 https://blog.csdn.net/Hehuyi_In/article/details/102992065 https://blog.c ...

  9. postgresql源码学习(57)—— pg中的四种动态库加载方法

    一. 基础知识 1. 什么是库 库其实就是一些通用代码,可以在程序中重复使用,比如一些数学函数,可以不需要自己编写,直接调用相关函数即可实现,避免重复造轮子. 在linux中,支持两种类型的库: 1. ...

  10. postgresql源码学习(一)—— 源码编译安装与gdb调试入门

    一. postgresql源码编译安装 因为只是用来调试的测试环境,把基本的软件装好和库建好就可以,一切从简. 1. 创建用户和目录 mkdir -p /data/postgres/base/ mkd ...

最新文章

  1. python语言介绍-Python语言简介
  2. linux nameserver导致的故障
  3. C# ListView 简单命令例子
  4. 计算机上没有启动程序怎么办,Win7开机不加载启动项怎么办
  5. linux 文档编辑器 word,linux下vi编辑器命令大全
  6. 简述python定义中的五个要点_Python基础知识复习
  7. 百度:在O(1)空间复杂度范围内对一个数组中前后连段有序数组进行归并排序
  8. 机房收费系统个人重构版:透过文档谈文档驱动开发
  9. MFC截图工具(不断更新)
  10. matlab接口编程配置,MATLAB与外部程序接口编程
  11. 使用 MonoGame* 开发游戏
  12. mysql事务保证幂等_事务与一致性:刚性or柔性
  13. 助力疫情防控,无接触式Beacon蓝牙考勤打卡方案
  14. datatables rows selected
  15. WP模板Ripro9.0免扩展二开版+全解密无后门
  16. 割平面法(Cutting Planes )
  17. 怎么把pdf转换成excel转换器免费版
  18. 电源:buck/boost/buck-boost相关计算公式
  19. 2014-04网易、微软、百度、腾讯、阿里实习生招聘经验与经过
  20. 2021年“金三银四”来袭!2021年阿里+头条+腾讯大厂Android笔试真题,含答案解析

热门文章

  1. 百度的AI技术定力终见真章
  2. Linux环境下——C语言聊天室项目
  3. 常见的一些代码编辑器
  4. web网页设计实例作业 ——电影泰坦尼克号(4页) HTML+CSS+JavaScript 学生HTML个人网页作业作品下载 个人网页设计制作 大学生个人网站作业模板 简单个人网页制作
  5. 研究人员开发实时歌词生成技术以激发歌曲创作灵感
  6. 双目九视清哺光仪_岳清江|坚定信念,普通人也能拥有非凡人生——【管鹏企业家书友会】...
  7. 小李飞刀系列之Oracle EBS期间平均成本(PAC)--生产成本计算(一)基础
  8. 前端解决手机拍照旋转问题及图片压缩上传
  9. 门禁上的push是什么意思_门禁门磁线接在什么设备上的
  10. 超越源域的攻击:面向黑盒域制作对抗样本