postgresql源码学习(九)—— 常规锁②-强弱锁与Fast Path
一、 强锁与弱锁
根据兼容性表,彼此相容的3个锁(1-3级,AccessShareLock、RowShareLock、RowExclusiveLock)是弱锁,4级锁ShareUpdateExclusiveLock比较特殊,既不是强锁也不是弱锁,5-8级的4个则是强锁。
弱锁只保存在当前会话,从而避免频繁访问共享内存的主锁表,提高数据库的性能。虽然判断是否有强锁也需要访问共享内存中的FastPathStrongRelationLocks,但这种访问粒度比较小。
二、 Fast Path检查
按照常规锁获取步骤,如果本地锁表中没有找到这个锁对象和锁模式,就需要进到第二步Fast Path检查。
1. 检查条件
Fast Path检查分两部分:弱锁部分与强锁部分,pg分别使用两个宏来判断(lock.c)。
// 弱锁需要满足4个条件:
// 1. 默认lock method;2. 对表加锁;3. 当前db;4. 锁模式小于ShareUpdateExclusiveLock(4级锁)
#define EligibleForRelationFastPath(locktag, mode) \((locktag)->locktag_lockmethodid == DEFAULT_LOCKMETHOD && \(locktag)->locktag_type == LOCKTAG_RELATION && \(locktag)->locktag_field1 == MyDatabaseId && \MyDatabaseId != InvalidOid && \(mode) < ShareUpdateExclusiveLock)
// 强锁也需要满足4个条件:
// 只有第4个为锁模式大于ShareUpdateExclusiveLock(4级锁),其余3个与弱锁相同
#define ConflictsWithRelationFastPath(locktag, mode) \((locktag)->locktag_lockmethodid == DEFAULT_LOCKMETHOD && \(locktag)->locktag_type == LOCKTAG_RELATION && \(locktag)->locktag_field1 != InvalidOid && \(mode) > ShareUpdateExclusiveLock)
注意判断的时候都没有mode = ShareUpdateExclusiveLock,因为它既不是强锁也不是弱锁。
2. 弱锁申请时
一个弱锁能够直接获得而不经过主锁表,需要判断两个条件:
- 共享内存中是否设置了强锁标记
如果共享内存中没有设置强锁标记,说明当前没有其他事务获得过这个锁对象的强锁模式。因此事务在这个对象间不会有冲突,此时可以考虑获得这个弱锁。
- 当前进程是否还有空间可以保存弱锁
如果弱锁保存在本事务的fast path中,实际上它是保存在PGPROC中的(以下代码在proc.h)。
#define FP_LOCK_SLOTS_PER_BACKEND 16/* Lock manager data, recording fast-path locks taken by this backend. */LWLock fpInfoLock; /* protects per-backend fast-path state */uint64 fpLockBits; /* lock modes held for each fast-path slot */Oid fpRelId[FP_LOCK_SLOTS_PER_BACKEND]; /* slots for rel oids */
PGPROC->fpRelId是一个长度为16的数组,里面保存的是表的oid,因此最多保存16个弱锁。
PGPROC->fpLockBits保存的是一个位图,目前64位中(uint64)只用了48位。每3位组成一个槽(因为共有3种弱锁),每个槽都与PGPROC->fpRelId数组中的表对应(所以3*16=48)。
/* Macros for manipulating proc->fpLockBits,3种弱锁所以每3位组成一个槽*/
#define FAST_PATH_BITS_PER_SLOT 3
#define FAST_PATH_LOCKNUMBER_OFFSET 1
//形式为111的掩码
#define FAST_PATH_MASK ((1 << FAST_PATH_BITS_PER_SLOT) - 1)
//得到第n个槽中已有的锁模式
#define FAST_PATH_GET_BITS(proc, n) \(((proc)->fpLockBits >> (FAST_PATH_BITS_PER_SLOT * n)) & FAST_PATH_MASK)
所以当一个事务要对弱锁使用fast path时,就尝试在PGPROC->fpLockBits中记录当前的锁模式,事务就能够获得锁了。
当然,如果记录的弱锁数超过了16,那也需要去主锁表中检查。
3. 强锁申请时
如果要申请的强锁模式,需要做的事如下:
- 设置强锁标记,即 FastPathStrongRelationLocks->count(也是一个数组)的引用计数加1
- 把其他事务保存的对应弱锁转移到主锁表中,方便死锁检测(因为有强锁之后就会有等待)
源码实现部分较长,为了结构清晰一些,我们单独放下面。
三、 强锁申请时的fast path操作
1. 设置强锁标记
- FastPathStrongRelationLocks的定义
typedef struct
{slock_t mutex;uint32 count[FAST_PATH_STRONG_LOCK_HASH_PARTITIONS]; //数组最大长度1024
} FastPathStrongRelationLockData;static volatile FastPathStrongRelationLockData *FastPathStrongRelationLocks;
- 强锁获取函数
核心是FastPathStrongRelationLocks->count[fasthashcode]++; 以及 locallock->holdsStrongLockCount = true;
/** BeginStrongLockAcquire - inhibit use of fastpath for a given LOCALLOCK,* and arrange for error cleanup if it fails*/
static void
BeginStrongLockAcquire(LOCALLOCK *locallock, uint32 fasthashcode)
{Assert(StrongLockInProgress == NULL);Assert(locallock->holdsStrongLockCount == false);/** Adding to a memory location is not atomic, so we take a spinlock to* ensure we don't collide with someone else trying to bump the count at* the same time.** XXX: It might be worth considering using an atomic fetch-and-add* instruction here, on architectures where that is supported.*/SpinLockAcquire(&FastPathStrongRelationLocks->mutex);FastPathStrongRelationLocks->count[fasthashcode]++; //注意这里,强锁引用计数加1locallock->holdsStrongLockCount = true;StrongLockInProgress = locallock;SpinLockRelease(&FastPathStrongRelationLocks->mutex);
}
2. 弱锁转移到主锁表中
把其他事务保存的对应弱锁转移到主锁表中。整体就是3个大循环:检查每个进程中的、每个表的、3种弱锁模式是否设置过。
/** FastPathTransferRelationLocks* Transfer locks matching the given lock tag from per-backend fast-path* arrays to the shared hash table.** Returns true if successful, false if ran out of shared memory.*/
static bool
FastPathTransferRelationLocks(LockMethod lockMethodTable, const LOCKTAG *locktag,uint32 hashcode)
{LWLock *partitionLock = LockHashPartitionLock(hashcode);Oid relid = locktag->locktag_field2;uint32 i;
/** Every PGPROC that can potentially hold a fast-path lock is present in* ProcGlobal->allProcs. Prepared transactions are not, but any* outstanding fast-path locks held by prepared transactions are* transferred to the main lock table.
* 每个PGPROC都有可能持有fast-path锁(在ProcGlobal->allProcs数组中),因此需要循环遍历所有PGPROC。所有prepared阶段之后的事务需要将fast-path locks转移到主锁表。*/for (i = 0; i < ProcGlobal->allProcCount; i++){PGPROC *proc = &ProcGlobal->allProcs[i];uint32 f;LWLockAcquire(&proc->fpInfoLock, LW_EXCLUSIVE);/** If the target backend isn't referencing the same database as the* lock, then we needn't examine the individual relation IDs at all;* none of them can be relevant.
如果进程引用的库跟要求加锁的对象都不是同一个,那么不需再检查其中的表,直接跳过本次循环,换下一个检查
*/ if (proc->databaseId != locktag->locktag_field1){LWLockRelease(&proc->fpInfoLock);continue;}/* * 如果是同一个库,那么遍历16个针对表的槽*/for (f = 0; f < FP_LOCK_SLOTS_PER_BACKEND; f++){uint32 lockmode;/* 如果不是同一个表,或者当前没有弱锁,则跳出循环,换下一个表 */if (relid != proc->fpRelId[f] || FAST_PATH_GET_BITS(proc, f) == 0)continue;/* 否则,继续按lockmode循环检查3类弱锁。FAST_PATH_LOCKNUMBER_OFFSET=1,FAST_PATH_BITS_PER_SLOT=3,所以其实就是for (lockmode = 1; lockmode < 1+3; ++lockmode)*/LWLockAcquire(partitionLock, LW_EXCLUSIVE);for (lockmode = FAST_PATH_LOCKNUMBER_OFFSET;lockmode < FAST_PATH_LOCKNUMBER_OFFSET + FAST_PATH_BITS_PER_SLOT;++lockmode){PROCLOCK *proclock;/* 检查3个弱锁的标记是否被设置过,如果没有,代表fast path中没出现过这个锁模式,换下一个 */if (!FAST_PATH_CHECK_LOCKMODE(proc, f, lockmode))continue;/* 否则,把这个锁模式挪到共享内存。SetupLockInTable函数的主要作用就是在主锁表和进程锁表中查找对应的锁,如果该锁不存在,则在主锁表和进程锁表中申请内存保存锁并初始化锁信息。我们会在下篇中重点学习这个函数。*/proclock = SetupLockInTable(lockMethodTable, proc, locktag,hashcode, lockmode);if (!proclock) // Returns false if ran out of shared memory{LWLockRelease(partitionLock);LWLockRelease(&proc->fpInfoLock);return false;}// 增加本地锁的计数 GrantLock(proclock->tag.myLock, proclock, lockmode);// 在PGPROC中去掉对应锁模式,业务PGPROC已转移到主锁表FAST_PATH_CLEAR_LOCKMODE(proc, f, lockmode);}LWLockRelease(partitionLock);/* No need to examine remaining slots. */break;}LWLockRelease(&proc->fpInfoLock);}return true;
}
参考
《PostgreSQL技术内幕:事务处理深度探索》第2章
postgresql源码学习(九)—— 常规锁②-强弱锁与Fast Path相关推荐
- postgresql源码学习(十三)—— 行锁①-行锁模式与xmax
一. 四种行锁 1. 简介与兼容性分析 pg采用元组级常规锁+xmax结合的方式实现行锁.我们曾经提到过常规锁是有很多类TAG的(typedef enum LockTagType),其中 LOCKTA ...
- postgresql源码学习(27)—— 事务日志⑦-日志落盘上层函数 XLogFlush
一. 预备知识 1. XLOG什么时候需要落盘 事务commit之前 log buffer被覆盖之前 后台进程定期落盘 2. 两个核心结构体 这两个结构体定义代码在xlog.c,它们在日志落盘过程中非 ...
- postgresql源码学习(51)—— 提交日志CLOG 原理 用途 管理函数
一. CLOG是什么 CLOG(commit log)记录事务的最终状态. 物理上,是$PGDATA/pg_xact目录下的一些文件 逻辑上,是一个数组,下标为事务id,值为事务最终状态 1. 事务最 ...
- postgresql源码学习(49)—— MVCC⑤-cmin与cmax 同事务内的可见性判断
一. 难以理解的场景 postgresql源码学习(十九)-- MVCC④-可见性判断 HeapTupleSatisfiesMVCC函数_Hehuyi_In的博客-CSDN博客 在前篇的可见性判断中有 ...
- PostgreSQL源码学习(1)--PG13代码结构
PostgreSQL源码学习(1)–PG13代码结构 PostgreSQL代码结构 Bootstrap:用于支持Bootstrap运行模式,该模式主要用来创建初始的模板数据库. Main:主程序模块, ...
- PostgreSQL源码学习(一)编译安装与GDB入门
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 PostgreSQL源码学习(一)编译安装与GDB入门 前言 一.安装PostgreSQL 1.获取源码 2.配置 3.编译 3.安装 ...
- postgresql源码学习(52)—— vacuum①-准备工作与主要流程
关于vacuum的基础知识,参考,本篇从源码层继续学习 https://blog.csdn.net/Hehuyi_In/article/details/102992065 https://blog.c ...
- postgresql源码学习(53)—— vacuum②-lazy vacuum之heap_vacuum_rel函数
一. table_relation_vacuum函数 1. 函数定义 前篇最后(https://blog.csdn.net/Hehuyi_In/article/details/128749517),我 ...
- postgresql源码学习(57)—— pg中的四种动态库加载方法
一. 基础知识 1. 什么是库 库其实就是一些通用代码,可以在程序中重复使用,比如一些数学函数,可以不需要自己编写,直接调用相关函数即可实现,避免重复造轮子. 在linux中,支持两种类型的库: 1. ...
最新文章
- JSP第二次作业_8小题
- Java 内存模型 与 高效并发
- 2019面试跳槽回顾和一点感想
- Linux下samba服务的错误处理
- 中兴a2018拆机图片_中兴天机拆机步骤详解【图文】
- Moss/Sharepoint 为特定用户组设置特定视图以及可见视图,自定义列表新建页,修改页和显示页(无代码法)...
- VMware网络连接方式(Host-only、NAT、Bridged)介绍及NAT环境下静态IP配置
- Python OpenCV显示图像并保存图像
- Newtonsoft.Json序列化和反序列之javascriptConvert.SerializeObject,DeserializeObject,JsonWriter,JsonReader...
- 种子文件多服务器,别再问我什么是 BT 种子了!一次性全告诉你
- 金蝶K3 webservice接口
- 微信小程序开发工具编辑样式文件后模拟器不显示
- NPS - 数字化营销 - 净推荐值
- 怎么用计算机算lnx,ln计算(log计算器在线)
- 用360查看本地dns
- UIUC简介 -- 写给今年得到UIUC Offer并在犹豫中的同学
- Shell 脚本 — 多行注释、开启子/不开启子进程执行、转义带颜色输出、读取键盘输入、输入输出重定向、单双引号、命令替换、读取变量、系统变量、正则过滤、算术运算、一行多条命令、字符串比较
- 网站降权根服务器有关系吗,导致网站降权或被k的原因有哪些?
- python进行数据处理_用python进行数据分析(二:数据处理)
- 爱立信软件测试英语笔试题,爱立信测试平台(dallas)开发岗位offer咨询