openGauss源码解析(78)

openGauss源码解析:事务机制源码解析(9)

5.3.3 常规锁

常规锁是使用哈希表实现的。常规锁支持多种锁模式(lock modes),这些锁模式之间的语义和冲突是通过冲突表来定义的。常规锁主要用于业务访问的数据库对象加锁。常规锁的加锁遵守数据库的两阶段加锁协议,即访问过程中加锁,事务提交时释放锁。

常规锁有等待队列并提供了死锁检测机制,当检测到死锁发生时选择一个事务进行回滚。

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

#define AccessShareLock 1 /* SELECT语句 */

#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所示,其中表示两个锁操作可以并发。

表5-7 锁冲突及并发控制

锁级别

1

2

3

4

5

6

7

8

1.ACCESS SHARE

-

2.ROW SHARE

-

-

3.ROW EXCLUSIVE

-

-

-

-

4.SHARE UPDATE EXCLUSIVE

-

-

-

-

-

5.SHARELOCK

-

-

-

-

-

6.SHARE ROW EXCLUSIVE

-

-

-

-

-

-

7.EXCLUSIVE

-

-

-

-

-

-

-

8.ACCESS EXCLUSIVE

-

-

-

-

-

-

-

-

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

typedef struct LOCKTAG {

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是将该锁所有的持有、等待线程串联起来的结构体指针。对应的代码如下:

typedef struct LOCK {

/* 哈希键 */

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结构,主要是将同一锁对象等待和持有者的线程信息串联起来的结构体。对应的代码如下:

typedef struct PROCLOCK {

/* 标识 */

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

/* 数据 */

LOCKMASK holdMask; /* 已获取锁类型的位掩码 */

LOCKMASK releaseMask; /* 预释放锁类型的位掩码 */

SHM_QUEUE lockLink; /* 指向锁对象链表的指针 */

SHM_QUEUE procLink; /* 指向PGPROC链表的指针 */

} PROCLOCK;

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

http://image.huawei.com/tiny-lts/v1/images/026fc2522fb00fc36e96_584x360.jpg@900-0-90-f.jpg

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

常规锁的主要函数如下。

(1) LockAcquire:对锁对象加锁。
(2) LockRelease:对锁对象释放锁。

(3 )LockReleaseAll:释放所有锁资源。

posted @ 2024-04-30 09:54  openGauss-bot  阅读(10)  评论(0编辑  收藏  举报