postgresql源码学习(十三)—— 行锁①-行锁模式与xmax
一、 四种行锁
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 读该行键值,但允许对除键外的其他字段更新。在外键检查时使用该锁 |
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相关推荐
- postgresql源码学习(九)—— 常规锁②-强弱锁与Fast Path
一. 强锁与弱锁 根据兼容性表,彼此相容的3个锁(1-3级,AccessShareLock.RowShareLock.RowExclusiveLock)是弱锁,4级锁ShareUpdateExclus ...
- postgresql源码学习(51)—— 提交日志CLOG 原理 用途 管理函数
一. CLOG是什么 CLOG(commit log)记录事务的最终状态. 物理上,是$PGDATA/pg_xact目录下的一些文件 逻辑上,是一个数组,下标为事务id,值为事务最终状态 1. 事务最 ...
- postgresql源码学习(27)—— 事务日志⑦-日志落盘上层函数 XLogFlush
一. 预备知识 1. XLOG什么时候需要落盘 事务commit之前 log buffer被覆盖之前 后台进程定期落盘 2. 两个核心结构体 这两个结构体定义代码在xlog.c,它们在日志落盘过程中非 ...
- PostgreSQL源码学习(1)--PG13代码结构
PostgreSQL源码学习(1)–PG13代码结构 PostgreSQL代码结构 Bootstrap:用于支持Bootstrap运行模式,该模式主要用来创建初始的模板数据库. Main:主程序模块, ...
- PostgreSQL源码学习(一)编译安装与GDB入门
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 PostgreSQL源码学习(一)编译安装与GDB入门 前言 一.安装PostgreSQL 1.获取源码 2.配置 3.编译 3.安装 ...
- postgresql源码学习(49)—— MVCC⑤-cmin与cmax 同事务内的可见性判断
一. 难以理解的场景 postgresql源码学习(十九)-- MVCC④-可见性判断 HeapTupleSatisfiesMVCC函数_Hehuyi_In的博客-CSDN博客 在前篇的可见性判断中有 ...
- postgresql源码学习(53)—— vacuum②-lazy vacuum之heap_vacuum_rel函数
一. table_relation_vacuum函数 1. 函数定义 前篇最后(https://blog.csdn.net/Hehuyi_In/article/details/128749517),我 ...
- postgresql源码学习(52)—— vacuum①-准备工作与主要流程
关于vacuum的基础知识,参考,本篇从源码层继续学习 https://blog.csdn.net/Hehuyi_In/article/details/102992065 https://blog.c ...
- postgresql源码学习(57)—— pg中的四种动态库加载方法
一. 基础知识 1. 什么是库 库其实就是一些通用代码,可以在程序中重复使用,比如一些数学函数,可以不需要自己编写,直接调用相关函数即可实现,避免重复造轮子. 在linux中,支持两种类型的库: 1. ...
- postgresql源码学习(一)—— 源码编译安装与gdb调试入门
一. postgresql源码编译安装 因为只是用来调试的测试环境,把基本的软件装好和库建好就可以,一切从简. 1. 创建用户和目录 mkdir -p /data/postgres/base/ mkd ...
最新文章
- python语言介绍-Python语言简介
- linux nameserver导致的故障
- C# ListView 简单命令例子
- 计算机上没有启动程序怎么办,Win7开机不加载启动项怎么办
- linux 文档编辑器 word,linux下vi编辑器命令大全
- 简述python定义中的五个要点_Python基础知识复习
- 百度:在O(1)空间复杂度范围内对一个数组中前后连段有序数组进行归并排序
- 机房收费系统个人重构版:透过文档谈文档驱动开发
- MFC截图工具(不断更新)
- matlab接口编程配置,MATLAB与外部程序接口编程
- 使用 MonoGame* 开发游戏
- mysql事务保证幂等_事务与一致性:刚性or柔性
- 助力疫情防控,无接触式Beacon蓝牙考勤打卡方案
- datatables rows selected
- WP模板Ripro9.0免扩展二开版+全解密无后门
- 割平面法(Cutting Planes )
- 怎么把pdf转换成excel转换器免费版
- 电源:buck/boost/buck-boost相关计算公式
- 2014-04网易、微软、百度、腾讯、阿里实习生招聘经验与经过
- 2021年“金三银四”来袭!2021年阿里+头条+腾讯大厂Android笔试真题,含答案解析
热门文章
- 百度的AI技术定力终见真章
- Linux环境下——C语言聊天室项目
- 常见的一些代码编辑器
- web网页设计实例作业 ——电影泰坦尼克号(4页) HTML+CSS+JavaScript 学生HTML个人网页作业作品下载 个人网页设计制作 大学生个人网站作业模板 简单个人网页制作
- 研究人员开发实时歌词生成技术以激发歌曲创作灵感
- 双目九视清哺光仪_岳清江|坚定信念,普通人也能拥有非凡人生——【管鹏企业家书友会】...
- 小李飞刀系列之Oracle EBS期间平均成本(PAC)--生产成本计算(一)基础
- 前端解决手机拍照旋转问题及图片压缩上传
- 门禁上的push是什么意思_门禁门磁线接在什么设备上的
- 超越源域的攻击:面向黑盒域制作对抗样本