一、 四种行锁

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就可以给你的头像戴上圣诞帽,别@微信团队了!
  2. DELL本本 执行SQL语句要FN+F5 如何更改成F5
  3. 运动想象脑机接口中迁移学习的完整流程
  4. python文件行数统计_文件行数和代码行数统计
  5. JAVA多线程总结(笔记)
  6. java接口文档生成工具_【分享】接口文档生成工具apipost
  7. 24日直播预告丨你们的“落落大神”来分享数据模型重构案例啦!
  8. tp3.2 php sdk上传七牛云
  9. 实现Apriori算法(python)
  10. Google开源的操作系统Fuchsia,专为大内存硬件设计
  11. My eclipse和Eclipse平台 JSP可视化编程工具
  12. Python调用科大讯飞语音合成离线SDK
  13. 通信系统仿真2-蒙特卡洛方法
  14. 什么是服务器的上行带宽和下行带宽
  15. Linux安全合规性检查和加固
  16. 由于哈希冲突,不同值的对象也可能具有相同的哈希值
  17. 《Mysql必知必会》
  18. kubernetes 网络
  19. 笔记本计算机名称PC2019,2019值得推荐的13寸笔记本电脑汇总
  20. Colab 上使用shutil.copytree()复制整个文件夹到另一个文件夹

热门文章

  1. matlab画图三维立体,matlab的三维图形绘制
  2. 小程序转uni-app——条件判断包含中文
  3. c++三大函数:拷贝构造(copy ctor)、拷贝赋值(copy op)、析构函数(dtor)
  4. 桌面Ubuntu卡死解决方案
  5. 解决attempted relative import with no known parent package问题
  6. 码码在线总结——java web开发
  7. 研究人员开发实时歌词生成技术以激发歌曲创作灵感
  8. 程序员的奋斗史(十二)——谈信念
  9. Mybatis-Plus(连接Hive)
  10. Vue 项目提示:`(Emitted value instead of an instance of Error) the “scope“ attribute for scoped slots