openGauss数据库源码解析系列文章——事务机制源码解析(四)

openGauss数据库源码解析系列文章——事务机制源码解析(四)

四.基于鲲鹏服务器的性能优化

本章着重介绍openGauss基于硬件结构的锁相关的函数及结构体的性能优化。

1. WAL Group inset优化

数据库redo日志缓存系统指的是数据库redo日志持久化的写缓存,数据库redo日志落盘之前会写入到日志缓存中再写到磁盘进行持久化。日志缓存的写入效率是决定数据库整体吞吐量的主要因素,而各个线程之间写日志时为了保证日志顺序写存在锁争抢,锁的争抢就成为了性能的主要瓶颈点。openGauss针对鲲鹏服务器ARM CPU的特点,通过group的方式进行日志的插入,减少锁的争抢,提升WAL日志的插入效率,从而提升整个数据库的吞吐性能。group的方式主要流程如图5所示。

图片

图5 group的方式日志插入

(1) 不需要所有线程都竞争锁。
(2) 在同一时间窗口所有线程在争抢锁之前先加入到一个group中,第一个加入group的线程作为leader。通过CAS原子操作来实现队列的管理。
(3) leader线程代表整个group去争抢锁。group中的其他线程(follower)开始睡眠,等待leader唤醒。
(4) 争抢到锁后,leader线程将group里的所有线程想要插入的日志遍历一遍得到需要空间总大小。leader线程只执行一次reserve space操作。
(5) leader线程将group中所有线程想要写入的日志都写入到日志缓冲区中。
(6) 释放锁,唤醒所有follower线程。
(7) follower线程由于需要写入的日志已经被leader写入,不需要再争抢锁,直接进入后续流程。

关键函数代码如下:

2. Cache align消除伪共享

CPU在访问主存时一次会获取整个缓存行的数据,其中x86典型值是64字节,而ARM 1620芯片L1和L2缓存都是64字节,L3缓存是128字节。这种数据获取方式本身可以大大提升数据访问的效率,但是假如同一个缓存行中不同位置的数据频繁被不同的线程读取和写入,由于写入的时候会造成其他CPU下的同一个缓存行失效,从而使得CPU按照缓存行来获取主存数据的努力不但白费,反而成为性能负担。伪共享就是指这种不同的CPU同时访问相同缓存行的不同位置的性能低效的行为。

当前锁逻辑中LWLock的访问仍然是最突出的热点之一。如果LWLOCK_PADDED_SIZE是32字节,且LWLock是按照一个连续的数组来存储的,对于64字节的缓存行可以同时容纳两个LWLockPadded,128字节的缓存行则可以同时含有4个LWLockPadded。当系统中对LWLock竞争激烈时,对应的缓存行不停地获取和失效,浪费大量CPU资源。故在ARM机器的优化下将padding_size直接设置为128,消除伪共享,提升整体LWLock的使用性能。

3. WAL INSERT 128CAS无锁临界区保护

目前数据库或文件系统,WAL需要把内存中生成的日志信息插入到日志缓存中。为了实现日志高速缓存,日志管理系统会并发插入,通过预留全局位置来完成,一般使用两个64位的全局数据位置索引分别表示存储插入的起始和结束位置,最大能提供16EB(Exabyte)的数据索引的支持。为了保护全局的位置索引,WAL引入了一个高性能的原子锁实现每个日志缓存位置的保护,在NUMA架构中,特别是ARM架构中,由于原子锁退避和高跨CPU访问延迟,缓存一致性性能差异导致WAL并发的缓存保护成为瓶颈。

优化的主要涉及思想是将两个64位的全局数据位置信息通过128位原子操作替换原子锁,消除原子锁本身在跨CPU访问、原子锁退避(backoff)、缓存一致性代价。如图6所示。

图片

图6 128CAS无锁临界区保护示意图

全局位置信息包括一个64位起始地址和一个64位的结束地址,将这两个地址合并成为一个128位信息,通过CAS原子操作完成免锁位置信息的预留。在ARM平台中没有实现128位的原子操作库,openGauss通过exclusive命令加载两个ARM64位数据来实现,ARM64汇编指令为LDXP/STXP。

关键数据结构及函数ReserveXLogInsertLocation的代码如下:

4. CLOG Partition优化

CLOG日志即是事务提交日志(详情可参考章节“事务ID分配及CLOG/CSNLOG)”,每个事务存在4种状态:IN_PROGRESS、COMMITED、ABORTED、SUB_COMMITED,每条日志占2 bit。CLOG日志需要存储在磁盘上,一个页面(8kB)可以包含215条,每个日志文件(段=256 x 8k)226条。当前CLOG的访问通过缓冲池实现,代码中使用统一的SLRU缓冲池算法。

图片

图7 CLOG的日志缓冲池优化前

### OpenGauss 数据库中临时表的源码解析 #### 临时表的概念与作用 临时表是在会话期间创建的一种特殊类型的表,仅对该会话可见。当会话结束时,临时表及其数据会被自动删除。这种特性使得临时表非常适合用于中间计算结果的存储。 #### 源码中的实现细节 在OpenGauss数据库中,临时表的管理主要由`ExecEndRecursiveUnion`函数处理的一部分逻辑来完成[^1]。该函数不仅负责清理递归查询过程中产生的资源,还涉及到了临时表的相关操作。具体来说: - **内存分配与释放**:对于临时表而言,在其生命周期内可能会涉及到大量的内存分配和释放工作。为了提高效率并减少碎片化问题,OpenGauss采用了特定的数据结构来进行管理和优化。 - **哈希表的应用**:特别是在去重场景下,系统利用了高效的哈希表机制来跟踪已经存在的记录,从而确保同一事务内的唯一性约束得到满足。 ```sql CREATE TEMPORARY TABLE temp_table ( id INT PRIMARY KEY, name VARCHAR(50) ); ``` 这段SQL语句展示了如何在一个具体的会话中创建一个名为`temp_table`的临时表,并定义了一个整数类型的主键字段`id`以及一个字符串类型的字段`name`。 #### 日志记录方式 值得注意的是,尽管临时表具有短暂的存在周期,但在某些情况下仍然会产生相应的日志条目。这些日志通常是以行级别的形式被记录下来,而不是像普通持久化对象那样依赖于更底层的物理页变更描述[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值