postgresql regular lock常规锁 烤的内嫩外焦,入口即化

news2024/11/28 10:35:20

专栏内容
postgresql内核源码分析
手写数据库toadb
并发编程
个人主页:我的主页
座右铭:天行健,君子以自强不息;地势坤,君子以厚德载物.

==================================

介绍

常规锁,主要用于数据库对象的加锁,如表,根据用户请求来加锁 。

它有死锁检测,在事务结束时会自动释放。

regular lock原理

regular lock像lwlock一样会预先在共享内存中分配,对于每一类型需要加锁的数据库对象都会分配一个锁对象。

为了标识具体的数据库对象,所以locktag唯一标识了每个锁,它的内容与加锁对象关联起来。 这里就有个问题,数据库对象可以非常多,锁的数量也很庞大,如何确定锁的数量呢?

regular lock 结构

typedef struct LOCK
{
	/* hash key */
	LOCKTAG		tag;			/* unique identifier of lockable object */

	/* data */
	LOCKMASK	grantMask;		/* bitmask for lock types already granted */
	LOCKMASK	waitMask;		/* bitmask for lock types awaited */
	dlist_head	procLocks;		/* list of PROCLOCK objects assoc. with lock */
	dclist_head waitProcs;		/* list of PGPROC objects waiting on lock */
	int			requested[MAX_LOCKMODES];	/* counts of requested locks */
	int			nRequested;		/* total of requested[] array */
	int			granted[MAX_LOCKMODES]; /* counts of granted locks */
	int			nGranted;		/* total of granted[] array */
} LOCK;

锁标识 LOCKTAG

锁标识的定义如下

typedef struct LOCKTAG
{
	uint32		locktag_field1; /* a 32-bit ID field */
	uint32		locktag_field2; /* a 32-bit ID field */
	uint32		locktag_field3; /* a 32-bit ID field */
	uint16		locktag_field4; /* a 16-bit ID field */
	uint8		locktag_type;	/* see enum LockTagType */
	uint8		locktag_lockmethodid;	/* lockmethod indicator */
} LOCKTAG;

locktag 唯一标识一个常规锁,它由6个成员组成, 前四个成员,在不同类型的锁中对应不同的数据

最后一个lockmethodid是定义锁的操作及冲突矩阵,一般采用默认的定义

LOCKTAG_RELATIONLOCKTAG
LOCKTAG_RELATIONlocktag_field1 = dboid 当relation 为共享表时 dboid = 0
locktag_field2 = reloid
LOCKTAG_RELATION_EXTEND同上
LOCKTAG_DATABASE_FROZEN_IDSlocktag_field1 = dboid
LOCKTAG_PAGElocktag_field1 = dboid
locktag_field2 = reloid
locktag_field3 = blocknum
LOCKTAG_TUPLElocktag_field1 = dboid
locktag_field2 = reloid
locktag_field3 = blocknum
locktag_field4 = offnum
LOCKTAG_TRANSACTIONlocktag_field1 = xid
locktag_field2 = 0
locktag_field3 = 0
locktag_field4 = 0
LOCKTAG_VIRTUALTRANSACTIONlocktag_field1 = (vxid).backendId
locktag_field2 = (vxid).localTransactionId
locktag_field3 = 0
locktag_field4 = 0
LOCKTAG_SPECULATIVE_TOKENlocktag_field1 = xid
locktag_field2 = token
locktag_field3 = 0
locktag_field4 = 0
LOCKTAG_OBJECTlocktag_field1 = dboid
locktag_field2 = classoid
locktag_field3 = objoid
locktag_field4 = objsubid
LOCKTAG_USERLOCK用户自定义锁
LOCKTAG_ADVISORYlocktag_field1 = id1
locktag_field2 = id2
locktag_field3 = id3
locktag_field4 = id4
LOCKTAG_APPLY_TRANSACTIONlocktag_field1 = dboid
locktag_field2 = suboid
locktag_field3 = xid
locktag_field4 = objid

锁流程

初始化

初始化主要获取锁的共享内存区域指针,并且创建本地记录已持有锁的hash空间。
另外还有一个全局hash记录锁持有者,在共享内存中存储。

锁的初始化接口如下

void InitLocks(void);
  • 获取锁的数量
  #define NLOCKENTS() \
	mul_size(max_locks_per_xact, add_size(MaxBackends, max_prepared_xacts))

  max_table_size = NLOCKENTS();
  init_table_size = max_table_size / 2;

每个backend可以最大持有锁的数量由GUC参数决定,最小10,最大为int上限

  • 在共享内存中分配锁的内存空间
  LockMethodLockHash = ShmemInitHash("LOCK hash",
									   init_table_size,
									   max_table_size,
									   &info,
									   HASH_ELEM | HASH_BLOBS | HASH_PARTITION);

常规锁在hash中存储,LockMethodLockHash的大小分配初始大小,不够时会再扩展,直到最大值。

  • 锁持有者记录的内存空间

锁持有者,按每个锁平均有两个持有者来计算,所以空间大小为

	max_table_size *= 2;
	init_table_size *= 2;

也是用hash链表来记录,在共享内存中分配

	LockMethodProcLockHash = ShmemInitHash("PROCLOCK hash",
										   init_table_size,
										   max_table_size,
										   &info,
										   HASH_ELEM | HASH_FUNCTION | HASH_PARTITION);
  • fastpath 信息记录的内存空间分配
	FastPathStrongRelationLocks =
		ShmemInitStruct("Fast Path Strong Relation Lock Data",
						sizeof(FastPathStrongRelationLockData), &found);
  • 每个backend自己也会记录自己持有锁信息
	LockMethodLocalHash = hash_create("LOCALLOCK hash",
									  16,
									  &info,
									  HASH_ELEM | HASH_BLOBS);

这里也是使用hash,内存是在当前本地内存分配,大小指定为16,这里对于重复持有锁只累积计数。

锁种类

typedef enum LockTagType
{
	LOCKTAG_RELATION,			/* whole relation */
	LOCKTAG_RELATION_EXTEND,	/* the right to extend a relation */
	LOCKTAG_DATABASE_FROZEN_IDS,	/* pg_database.datfrozenxid */
	LOCKTAG_PAGE,				/* one page of a relation */
	LOCKTAG_TUPLE,				/* one physical tuple */
	LOCKTAG_TRANSACTION,		/* transaction (for waiting for xact done) */
	LOCKTAG_VIRTUALTRANSACTION, /* virtual transaction (ditto) */
	LOCKTAG_SPECULATIVE_TOKEN,	/* speculative insertion Xid and token */
	LOCKTAG_OBJECT,				/* non-relation database object */
	LOCKTAG_USERLOCK,			/* reserved for old contrib/userlock code */
	LOCKTAG_ADVISORY,			/* advisory user locks */
	LOCKTAG_APPLY_TRANSACTION	/* transaction being applied on a logical
								 * replication subscriber */
} LockTagType;

当前使用regular lock 加锁的数据库对象,由LockTagType定义,每一种对象都有对应的加锁、解锁接口

常规锁接口

表锁相关接口

表锁被分为8级,每级定义了使用范围,同时按等级递增规定了冲突矩阵。

在open表时,使用OID或relid来对表进行加锁,也可以尝试加锁,不进行等待

extern void LockRelationOid(Oid relid, LOCKMODE lockmode);
extern void LockRelationId(LockRelId *relid, LOCKMODE lockmode);
extern bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode);
extern void UnlockRelationId(LockRelId *relid, LOCKMODE lockmode);
extern void UnlockRelationOid(Oid relid, LOCKMODE lockmode);

以下接口是open之后,进一步对表加锁时调用

此时relation结构可用,直接使用此结构就可以对表加锁

extern void LockRelation(Relation relation, LOCKMODE lockmode);
extern bool ConditionalLockRelation(Relation relation, LOCKMODE lockmode);
extern void UnlockRelation(Relation relation, LOCKMODE lockmode);
extern bool CheckRelationLockedByMe(Relation relation, LOCKMODE lockmode,
									bool orstronger);
extern bool LockHasWaitersRelation(Relation relation, LOCKMODE lockmode);

extern void LockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode);
extern void UnlockRelationIdForSession(LockRelId *relid, LOCKMODE lockmode);

表扩展时相关锁接口

extern void LockRelationForExtension(Relation relation, LOCKMODE lockmode);
extern void UnlockRelationForExtension(Relation relation, LOCKMODE lockmode);
extern bool ConditionalLockRelationForExtension(Relation relation,
												LOCKMODE lockmode);
extern int	RelationExtensionLockWaiterCount(Relation relation);

计算database frozenxid时的锁接口

extern void LockDatabaseFrozenIds(LOCKMODE lockmode);

每个数据库会计算 frozenxid,然后计算所有库的最小值作为集群的frozenxid;
每个database的值在 pg_database.datfrozenxid 字段中存储

锁定page 时的接口

extern void LockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode);
extern bool ConditionalLockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode);
extern void UnlockPage(Relation relation, BlockNumber blkno, LOCKMODE lockmode);

当前只在索引中使用

行锁接口

extern void LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode);
extern bool ConditionalLockTuple(Relation relation, ItemPointer tid,
								 LOCKMODE lockmode);
extern void UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode);

事务等待锁接口

extern void XactLockTableInsert(TransactionId xid);
extern void XactLockTableDelete(TransactionId xid);
extern void XactLockTableWait(TransactionId xid, Relation rel,
							  ItemPointer ctid, XLTW_Oper oper);
extern bool ConditionalXactLockTableWait(TransactionId xid);

主要用于事务隔离冲突时,等待某个事务xid结束时使用

VXID锁接口

extern void WaitForLockers(LOCKTAG heaplocktag, LOCKMODE lockmode, bool progress);
extern void WaitForLockersMultiple(List *locktags, LOCKMODE lockmode, bool progress);

用于 VXID冲突时进行等待

行插入锁接口

extern uint32 SpeculativeInsertionLockAcquire(TransactionId xid);
extern void SpeculativeInsertionLockRelease(TransactionId xid);
extern void SpeculativeInsertionWait(TransactionId xid, uint32 token);

其它数据库对象(除表外)

extern void LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
							   LOCKMODE lockmode);
extern void UnlockDatabaseObject(Oid classid, Oid objid, uint16 objsubid,
								 LOCKMODE lockmode);

跨库数据库对角

extern void LockSharedObject(Oid classid, Oid objid, uint16 objsubid,
							 LOCKMODE lockmode);
extern void UnlockSharedObject(Oid classid, Oid objid, uint16 objsubid,
							   LOCKMODE lockmode);

extern void LockSharedObjectForSession(Oid classid, Oid objid, uint16 objsubid,
									   LOCKMODE lockmode);
extern void UnlockSharedObjectForSession(Oid classid, Oid objid, uint16 objsubid,
										 LOCKMODE lockmode);

extern void LockApplyTransactionForSession(Oid suboid, TransactionId xid, uint16 objid,
										   LOCKMODE lockmode);
extern void UnlockApplyTransactionForSession(Oid suboid, TransactionId xid, uint16 objid,
											 LOCKMODE lockmode);										 

结尾

非常感谢大家的支持,在浏览的同时别忘了留下您宝贵的评论,如果觉得值得鼓励,请点赞,收藏,我会更加努力!

作者邮箱:study@senllang.onaliyun.com
如有错误或者疏漏欢迎指出,互相学习。

注:未经同意,不得转载!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/741945.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

学Python还是学JAVA,千万别听机构瞎吹!

机构真的为了割韭菜,无所不用其极,过份夸大Python语言的能力或者贬低JAVA。 导致大家要么跟风被割韭菜,学完也用不到。 导致这一主要原因: 1.你不懂行业内需求。 2.你缺乏认知清楚自己的发展规划路线的途径。3.对于编程的优缺点…

Pandas理论与实战(二)

本文章续接上篇文章 目录 1.数据抽取 1.1 抽取一行数据 1.2 抽取多行数据 1.3 抽取指定列数据 1.4 抽取指定行、列数据 1.5 按指定条件抽取数据 2、数据的增加、删除和修改 2.1 数据增加 2.2 修改数据 2.3 删除数据 1.数据抽取 数据分析过程中,并不是所有的数…

CentOS 6 手动将OpenSSH升级到9.3

前言 收到通知说服务器组件存在漏洞 服务器版本:CentOS-6.8-x86_64 目前SSH版本:OpenSSH_5.3p1, OpenSSL 1.0.1e-fips 11 Feb 2013 前置操作 为了避免升级过程中出现的意外导致服务器无法进行连接,建议对重要的内容先进行备份 创建快照 在主机服务商那里为主机创建快照,防止最糟…

Android Studio无法打开问题解决记录

目录 1 问题起因2 发现问题3 解决问题 1 问题起因 问题的起因是我为了运行一个Kotlin项目,但是报了一个错误: Kotlin报错The binary version of its metadata is 1.5.1, expected version is 1.1.16 然后我就上百度去搜了以下,一篇博客让禁用…

GEE:多元线性回归

作者:CSDN _养乐多_ 本文记录了在NDVI、EVI和LAI作为自变量,precipitation作为因变量的条件下,使用linearRegression函数进行线性回归分析的代码,代码在Google Earth Engine(GEE)平台上实现。具体而言&am…

AI大数据智能视频融合平台EasyCVR新增Ehome黑白名单配置

EasyCVR视频融合平台基于云边端智能协同架构,具有强大的数据接入、处理及分发能力,平台支持海量视频汇聚管理,可支持多协议接入,包括市场主流标准协议与厂家私有协议及SDK,如:国标GB28181、RTMP、RTSP/Onvi…

Linux 学习记录50(QT篇)

Linux 学习记录50(QT篇) 本文目录 Linux 学习记录50(QT篇)一、基于QT的TCP客户端连接1. 代码实现2. 自制的客户端/服务器 二、QT数据库SQL1. QT将数据库分为三个层次2. 实现数据库操作的相关方法 思维导图练习1.2.3.4. 一、基于QT的TCP客户端连接 所需头文件 <QTcpSocket&g…

3G理论概述

文章目录 UMTS&#xff08;通用移动通信系统&#xff0c;Universal Mobile Telecommunications System&#xff09;UTRAN&#xff08;UMTS陆地无线接入网&#xff0c;UMTS Terrestrial Radio Access Network&#xff09;RNC&#xff08;无线网络控制器&#xff0c;Radio Networ…

music21 层级解析(了解次结构方可将任意曲谱与mid互相转换)

这段代码创建了一个音乐乐谱并将其保存为 MIDI 文件&#xff0c;其中包含一个乐器和多个小节。每个小节中包含四个音符或和弦&#xff0c;然后将小节添加到乐谱中。最后&#xff0c;将乐谱写入 MIDI 文件。 首先&#xff0c;通过导入 music21 库来使用它的功能。 import music2…

onvif库封装及qt工程调用onvif库实现设备搜索、获取码流地址等功能

一、前言&#xff1a; 本篇的OnvifManager工程是在vs2010下进行开发编译&#xff0c;它实现了对onvif库的封装调用&#xff0c;目前工程接口实现了对onvif的搜索、码流地址获取、设备重启接口&#xff0c;其他接口后续可以通过更改工程代码进行添加。qt工程myonvif是对OnvifMan…

Zotero翻译插件“zotero pdf translate”——使用时出现广告的问题的解决办法

一、背景&#xff1a; 在Zotero上安装了“zotero-pdf-translate”插件来辅助翻译。今天忽然发现&#xff0c;在使用CNKI翻译引擎的时候&#xff0c;末尾被加上了广告&#xff1a; (查看名企职位领高薪offer&#xff01;--->智联招聘https***dict.cnki.net/ad.html) 如下&…

【IMX6ULL驱动开发学习】16.睡眠机制_poll机制_fasync异步通知(按键控制LED)

学完了休眠唤醒机制、poll机制、异步通知、定时器、tasklet、工作队列、mmap、input子系统后&#xff0c;该沉淀沉淀了 一、睡眠机制 案例&#xff1a;APP程序读取按键值 - 睡眠机制&#xff08;阻塞或非阻塞&#xff09; 1.等待队列头创建 static DECLARE_WAIT_QUEUE_HEA…

从零实现深度学习框架——带Attentiond的Seq2seq机器翻译

引言 本着“凡我不能创造的,我就不能理解”的思想,本系列文章会基于纯Python以及NumPy从零创建自己的深度学习框架,该框架类似PyTorch能实现自动求导。 要深入理解深度学习,从零开始创建的经验非常重要,从自己可以理解的角度出发,尽量不使用外部完备的框架前提下,实现我…

【Windows】PowerToys - 微软超强生产力工具

Windows用户必看 - 电脑系统增强工具 下载 下载地址 简介 PowerToys 是一组由 Microsoft 创建和维护的开源实用工具集&#xff0c;旨在为 Windows 操作系统提供额外的功能和增强体验。它是为 Windows 10 及更高版本设计的&#xff0c;可帮助用户提高效率、个性化和定制化他们…

apache 安装配置 基础篇(三) 之 虚拟机

apache虚拟主机类型有两种 1、基于名称的虚拟主机 2、基于地址或IP地址的虚拟主机 基于名称的虚拟主机 ## httpd-vhosts.conf 添加如下代码&#xff0c; ## 同时要在httpd.conf中追加监听89端口 Listen 89 ## 修改apache 文件要 在bin路径下面的cmd 录入 httpd -t 检测 代…

centos7搭建postgresql主从(主备)架构

本篇介绍如何在centos7系统搭建一个postgresql主备集群实现最近的HA&#xff08;高可用&#xff09;架构。后续更高级的HA模式都是基于这个最基本的主备搭建。 节点规划 ip 主机名 用途 192.168.182.4 node1 主库&#xff08;读写&#xff09; 192.168.182.5 node2 备库&#…

大神都在用的4个AI绘画工具,记得收藏

AI绘画工具是一种利用人工智能辅助绘画创作的工具&#xff0c;它可以帮助设计师快速绘制高质量的艺术作品。本文列出了国内外4款免费的AI绘画工具&#xff0c;它们使用起来非常简单&#xff0c;而且创作效果非常不错&#xff0c;一起来看看吧&#xff01; 1.即时灵感 即时灵感…

【字符串 简单】LeetCode 14. 最长公共前缀 Java

我的思路&#xff1a; 给字符串数组按照字符串的长度从长到短排序&#xff0c;因为最长公共前缀最长的话&#xff0c;也只能是字符串数组中最短的那一个字符串设置一个index变量&#xff0c;表示当前正在检查字符数组中所有字符串的index位置循环遍历字符串数组n遍&#xff0c…

Java设计模式之结构型-桥接模式(UML类图+案例分析)

目录 一、基础概念 二、UML类图 三、角色分析 四、案例分析 1、支付方式 2、支付渠道 五、总结 一、基础概念 桥接模式&#xff08;Bridge Pattern&#xff09;是一种结构型设计模式&#xff0c;其主要目的是“将抽象部分与实现部分分离&#xff0c;使它们都可以独立地…