事务机制源码解析| 锁机制(3)

2023年 12月 15日 67.4k 0

5.3.3 常规锁

常规锁是使用哈希表实现的。常规锁支持多种锁模式(lock modes),这些锁模式之间的语义和冲突是通过冲突表来定义的。常规锁主要用于业务访问的数据库对象加锁。常规锁的加锁遵守数据库的两阶段加锁协议,即访问过程中加锁,事务提交时释放锁。
常规锁有等待队列并提供了死锁检测机制,当检测到死锁发生时选择一个事务进行回滚。

openGauss提供了8个锁级别分别用于不同的语句并发:1级锁一般用于SELECT查询操作;3级锁一般用于基本的INSERT、UPDATE、DELETE操作;4级锁用于VACUUM、analyze等操作;8级锁一般用于各类DDL语句,具体宏定义及命名代码如下:

#define RowShareLock 2 /* SELECT FOR UPDATE/FOR SHARE语句 */
#define RowExclusiveLock 3 /* INSERT, UPDATE, DELETE语句 */
#define ShareUpdateExclusiveLock
4 /* VACUUM (non-FULL),ANALYZE, CREATE INDEX CONCURRENTLY语句 */
#define ShareLock 5 /* CREATE INDEX (WITHOUT CONCURRENTLY)语句 */
#define ShareRowExclusiveLock
6 /* 类似于独占模式, 但是允许ROW SHARE模式并发 */
#define ExclusiveLock
7 /* 阻塞ROW SHARE,如SELECT...FOR UPDATE语句 */
#define AccessExclusiveLock
8 /* ALTER TABLE, DROP TABLE, VACUUM FULL, LOCK TABLE语句 */

这8个级别的锁冲突及并发控制如表5-7所示,其中表示两个锁操作可以并发。

加锁对象数据结构,通过对field1->field5赋值标识不同的锁对象,使用locktag_type标识锁对象类型,如relation表级对象、tuple行级对象、事务对象等,对应的代码如下:

uint32 locktag_field1; /* 32比特位*/
uint32 locktag_field2; /* 32比特位*/
uint32 locktag_field3; /* 32比特位*/
uint32 locktag_field4; /* 32比特位*/
uint16 locktag_field5; /* 32比特位*/
uint8 locktag_type; /* 详情见枚举类LockTagType*/
uint8 locktag_lockmethodid; /* 锁方法类型*/
} LOCKTAG;

typedef enum LockTagType {
LOCKTAG_RELATION, /* 表关系*/
/* LOCKTAG_RELATION的ID信息为所属库的OID+表OID;如果库的OID为0表示此表是共享表,其中OID为openGauss内核通用对象标识符 */
LOCKTAG_RELATION_EXTEND, /* 扩展表的优先权*/
/* LOCKTAG_RELATION_EXTEND的ID信息 */
LOCKTAG_PARTITION, /* 分区*/
LOCKTAG_PARTITION_SEQUENCE, /* 分区序列*/
LOCKTAG_PAGE, /* 表中的页*/
/* LOCKTAG_PAGE的ID信息为RELATION信息+BlockNumber(页面号)*/
LOCKTAG_TUPLE, /* 物理元组*/
/* LOCKTAG_TUPLE的ID信息为PAGE信息+OffsetNumber(页面上的偏移量) */
LOCKTAG_TRANSACTION, /* 事务ID (为了等待相应的事务结束) */
/* LOCKTAG_TRANSACTION的ID信息为事务ID号 */
LOCKTAG_VIRTUALTRANSACTION, /* 虚拟事务ID */
/* LOCKTAG_VIRTUALTRANSACTION的ID信息为它的虚拟事务ID号 */
LOCKTAG_OBJECT, /* 非表关系的数据库对象 */
/* LOCKTAG_OBJECT的ID信息为数据OID+类OID+对象OID+子ID */
LOCKTAG_CSTORE_FREESPACE, /* 列存储空闲空间 */
LOCKTAG_USERLOCK, /* 预留给用户锁的锁对象 */
LOCKTAG_ADVISORY, /* 用户顾问锁 */
LOCK_EVENT_NUM
} LockTagType;

常规锁LOCK结构,tag是常规锁对象的唯一标识,procLocks是将该锁所有的持有、等待线程串联起来的结构体指针。对应的代码如下:

/* 哈希键 */
LOCKTAG tag; /* 锁对象的唯一标识 */

/* 数据 */
LOCKMASK grantMask; /* 已经获取锁对象的位掩码 */
LOCKMASK waitMask; /* 等待锁对象的位掩码 */
SHM_QUEUE procLocks; /* 与锁关联的PROCLOCK对象链表 */
PROC_QUEUE waitProcs; /* 等待锁的PGPROC对象链表 */
int requested[MAX_LOCKMODES]; /* 请求锁的计数 */
int nRequested; /* requested数组总数 */
int granted[MAX_LOCKMODES]; /* 已获取锁的计数 */
int nGranted; /* granted数组总数 */
} LOCK;

PROCLOCK结构,主要是将同一锁对象等待和持有者的线程信息串联起来的结构体。对应的代码如下:

/* 标识 */
PROCLOCKTAG tag; /* proclock对象的唯一标识 */

/* 数据 */
LOCKMASK holdMask; /* 已获取锁类型的位掩码 */
LOCKMASK releaseMask; /* 预释放锁类型的位掩码 */
SHM_QUEUE lockLink; /* 指向锁对象链表的指针 */
SHM_QUEUE procLink; /* 指向PGPROC链表的指针 */
} PROCLOCK;

t_thrd.proc结构体里waitLock字段记录了该线程等待的锁,该结构体中procLocks字段将所有跟该锁有关的持有者和等着串起来,其队列关系如图5-16所示。

图5-16 t_thrd.proc结构体队列关系图

常规锁的主要函数如下。
(1) LockAcquire:对锁对象加锁。
(2) LockRelease:对锁对象释放锁。
(3 )LockReleaseAll:释放所有锁资源。

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论