概述
事务
事务是为用户提供的最核心、最具吸引力的数据库功能之一。简单地说,事务是用户定义的一系列数据库操作(如查询、插入、修改或删除等)的集合,从数据库内部保证了该操作集合作为一个整体的原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),这些特性统称事务的ACID特性。
DBMS中的并发控制
并发控制旨在针对数据库中对事务并行的场景,保证 ACID 中的一致性(Consistency)与隔离性(Isolation)。数据库技术中主流的三种并发控制技术分别是: Multi-version Concurrency Control (MVCC), Strict Two-Phase Locking (S2PL), 以及 Optimistic Concurrency Control (OCC),每种技术也都有很多的变种。
MVCC
MVCC的基本机制是:写事务不会原地修改元组内容,每次写操作都会在旧的版本之上创建新的版本,并且会保留旧的版本。当某个事务需要读取数据时,数据库系统会从所有的版本中选取出符合该事务隔离级别要求的版本。
MVCC 的主要优点是读数据的锁请求与写数据的锁请求不冲突,以此来实现读不阻塞写,写也不阻塞读。
openGauss事务整体架构
在openGauss中,事务的实现与存储引擎的实现有很强关联,代码主要集中在src/gausskernel/storage/access/transam及src/gausskernel/storage/lmgr下,关键文件如图所示。
(1) 事务管理器:事务系统的中枢,它的实现是一个有限循环状态机,通过接受外部系统的命令并根据当前事务所处的状态决定事务的下一步执行过程。
(2) 日志管理器:用来记录事务执行的状态以及数据变化的过程,包括事务提交日志(CLOG)、事务提交序列日志(CSNLOG)以及事务日志(XLOG)。其中CLOG日志只用来记录事务执行的结果状态,CSNLOG记录日志提交的顺序,用于可见性判断;XLOG是数据的redo日志,用于恢复及持久化。
(3) 线程管理机制:通过一片内存区域记录所有线程的事务信息,任何一个线程可以通过访问该区域获取其他事务的状态信息。
(4) MVCC机制:openGauss系统中,事务执行读流程结合各事务提交的CSN序列号,采用了多版本并发控制机制,实现了元组的读和写互不阻塞。
(5) 锁管理器:实现系统的写并发控制,通过锁机制来保证事务写流程的隔离性。
MVCC的实现
我们需要关注:
- 元组版本号的实现
- 快照的实现
- 判断数据有效性、可见性、可更新性的算法的实现
- 不同的隔离级别的实现
多版本元组存储结构
src/include/access/htup.h
为了定义MVCC 中不同版本的数据,Opengauss在每个元组的头部信息HeapTupleHeaderData中引入了一些字段如下:
typedef struct HeapTupleHeaderData {
union {
HeapTupleFields t_heap; /* 存储该元组的一些描述信息 */
DatumTupleFields t_datum;
} t_choice;
ItemPointerData t_ctid; /* (块号,块内偏移) 存储用来记录当前元组或新元组的物理位置 */
/* Fields below here must match MinimalTupleData! */
uint16 t_infomask2;
uint16 t_infomask; /* various flag bits, see below */
uint8 t_hoff;
/* ^ - 23 bytes -