GaussDB关键技术原理:高性能(四)从USTORE存储引擎、计划缓存计划技术、数据分区与分区剪枝、列式存储和向量化引擎、SMP并行执行等五方面对高性能关键技术进行解读,本篇将从LLVM动态查询编译执行、SQL-BYPASS执行优化、线程池化、多核处理器优化、日志无锁刷新与多级流水等方面继续介绍GaussDB高性能关键技术,并对高斯数据库性能优化进行总结。
3.11 LLVM动态查询编译执行
在传统经典执行器算子中基于遍历树的表达式计算框架,这种框架的好处是清晰明了,但是在性能上却不是最优的,主要有以下几个原因:
(1)表达式计算其框架的通用性决定了其执行模式要适配各种不同的操作符和数据类型,因此在运行时要根据其表达式遍历的具体结果来确定其执行的函数和类型,对这些类型的判断要引入非常多的分支判断。
(2)表达式计算在整体的执行过程中要进行多次的函数调用,其调用的深度取决于其树的深度,这一部分也有着非常大的开销。
这两个核心原因,分支判断和函数调用同样在执行算子中也是影响性能的关键因素,为了提升其执行速度,GaussDB引入了业界著名的开源编译框架LLVM(Low Level Virtual Machine)来提速的执行速度,LLVM是一个通用的编译框架,能够支持不同的计算平台。LLVM提升整体表达式计算的核心要点如下:
(1)GaussDB内置的LLVM编译框架通过为每一个计算单元(表达式或者执行算子里面的热点函数)生成一段独特的执行代码,由于在编译的时候提前知道了表达式涉及的操作和数据类型,为这个表达式生成的执行代码将所有的逻辑内联,完全去除函数调用。GaussDB内置的LLVM编译为这个表达式生成了一段特殊代码。里面已经没有任何其他的函数调用,所有的函数都已经被内联在一起,同时去掉了关于数据类型的分支判断。
(2)LLVM编译框架利用编译技术最大程度的让生成的代码将中间结果的数据存储在CPU寄存器里,让数据读取的速度加快。
LLVM通过消除条件逻辑冗余,降低虚函数调用次数,改善数据locality,可大大降低任务执行代价; LLVM生成的machine code为一次性成本,整个执行过程均可使用,数据量越大,所获取的收益将越大,因此对于一个可以实施LLVM动态编译优化的查询,其收益随着数据量的增多会逐步增加,对应的性能提升比例也会越来越大
3.12 SQL-BYPASS执行优化
在典型的OLTP场景中,简单查询占了很大一部分比例。这类查询本身大多数会走索引、分区剪支、只涉及单表和简单表达式的查询,这类查询的有效数据读取占比在这个查询解析、执行过程中并不大,即便使用计划缓存技术(PBE)把查询解析部分省下来以后,执行器初始化exec_init_*()仍然会有比较明显的开销,例如下面火焰图所profiling的场景,执行器初始化开销占比超过40%,并且每个同样的模板查询这部分的操作都是无效的重复开销,有效部分只有Operator::get_next(),占比只有15%左右。
因此为了加速这类查询,提出了SQL-BY-PASS框架,其核心思想是对执行路径inline处理优化,减少不必要的执行器函数迭代的开销,在parse层对这类查询做简单的模式判别后,进入到特殊的执行路径里,跳过经典的执行器执行框架,包括算子的初始化与执行、表达式与投影等经典框架,直接重写一套简洁的执行路径,并且直接调用存储接口,这样可以大大加快简单查询的执行速度。以分区表点差举例,通过SqlByPass技术将分区表的执行过程进行扁平化inline处理,将原来SQL执行引擎中很深的调用栈扁平化,性能提升30%。
3.13 线程池化
在OLTP领域中数据库通常需要处理大量的客户端连接。因此,高并发场景的处理能力是数据库的重要能力之一。对于外部连接最简单的处理模式是per-thread-per-connection模式,即来一个用户连接产生一个线程。这个模式好处是架构上处理简单,但是高并发下,由于线程太多,线程切换和数据库轻量级锁区域的冲突过大导致性能急剧下降,使得系统性能(吞吐量)严重下降,无法满足用户性能的SLA。归纳起来每当面对高并发业务(线程/CPU核数比大于10)场景时,系统通常面临以下几个方面挑战:
(1)资源耗尽,由于接入的会话数过多导致消耗过多的内存、连接数资源耗尽产生系统宕机、OOM等异常,造成系统可用性降低。
(2)过度争抢,由于并发数过多而具体CPU计算有限,增加了临界区锁、信号量等处理开销,大量资源并未产生客户实际吞吐量。
(3)相互影响,如果某一个会话请求占用过多的CPU、内存资源导致对其他会话资源的挤占,造成单个请求拖垮整个系统的严重问题。
因此,对于高并发性能稳定性从本质上分析,数据库系统需要从设计上解耦接入会话数与资源使用量&争抢之间的线性耦合关系,同时对极端"炸弹式"请求需要有有效的管控措施,将其影响控制在有限的范围内。GaussDB主要的解决方案过线程资源池化复用的技术来解决该问题。线程池技术的整体设计思想是线程资源池化,并且在不同连接之间复用。系统在启动之后会根据当前核数或者用户配置,启动固定一批数量的工作线程,一个工作线程会服务一到多个连接session,这样把session和thread进行了解耦。因为工作线程数是固定的,因此在高并发下不会导致线程的频繁切换,而由数据库层来进行session的调度管理。
高斯数据库线程池实现主要体现在以下3个方面:
(1)资源解耦:将接入会话&工作线程进行解耦隔离,确保高并发场景下系统资源不会持续增加而是保持稳定。
(2)多核优化:在多核场景下考虑到NUMA效应,线程池针对工作线程的调度范围根据NUMA node进行分配和限制,避免线程的调度跨numa,降低处理时延提升性能。
(3)资源管控:线程池化基础上实现以工作线程为粒度的过载管控,避免单一线程占用过多资源,提升系统可靠性。
通过线程池化改进后相比高并发场景下具有明显的性能稳定性,以下是数据库性能稳定性测试,通过标准OLTP负载模型(TPCC)的测试结果。
|---------|---------------|------------------|
| 并发数 | MySQL非线程池 | GaussDB(线程池) |
| 100 | 339,171.29 | 852,701.91 |
| 200 | 401,879.30 | 1,267,780.87 |
| 400 | 380,815.54 | 1,616,192.10 |
| 600 | 344,775.51 | 1,693,294.49 |
| 800 | 316,093.14 | 1,663,179.51 |
| 1600 | 250,559.43 | 1,632,902.63 |
| 2400 | 207,585.54 | 1,631,816.55 |
| 3200 | 181,735.22 | 1,615,253.23 |
总结:在线程池的帮助下数据库系统面对大并发场稳定性、性能两个方面都有明显提升。
(1)稳定性:随着并发数增加MySQL无线程模式的数据库性能在CPU资源满了以后并不会像GaussDB这样保持稳定。
(2)高性能:数据库线程池化考虑多核CPU NUMA亲和以后进一步提升性能,相比MySQL有明显优势。
3.14 多核处理器优化
鲲鹏ARM服务器多CPU-socket架构下跨NUMA内存访问延迟存在严重的不对称,远/近端内存访存时延有成倍数差异,同时相比x86内存访问时延高50%、并发控制原语代价高2-3倍,在数据库中会以进一步恶化OLTP瓶颈,尽管通常在架构下CPU物理核心数相比x86有了一定提升,但如果不合理设计和实现数据库内核关键数据结构、线程调度模型无法充分利用ARM多核的优势,如何优化NUMA带来的访问时延问题,如何充分利用众核CPU解决并发控制问题成为了鲲鹏上优化数据库OLTP负载性能的主要挑战。使用标准事务型负载TPMC benchmark测试结果96核x86 135w,128核ARM在NUMA优化前只有110w tpmC左右。
GaussDB Kernel根据ARM处理器的多核NUMA架构特点,进行针对性一系列NUMA架构相关优化,主要围绕三个方面进行:
(1)线程调度访存本地化:减少跨核内存访问的时延问题,让线程调度尽可能在单个NUMA节点内,同时针对高频热点数据结构通过适当的冗余保留在本地,减少跨NUMA远端内存访问。
(2)ARM多核算力优化:针对鲲鹏ARM体系下计算核心多的优势,对数据库查询处理、数据库缓冲区脏页处理、WAL日志等关键处理流程进行multi-thread多级流水线改造,将原有单线程处理流程下发给多个CPU-线程进行并行处理提升总体性能。
(3)ARM指令集优化:借助ARMv8.1引入的新的原子操作LSE,将大量的可转换为原子类型操作(plus、minus、CAS)的部分,替换为ARM硬件相关原语LSE指令集,从而提升多线程间同步性能,例如工作线程-WAL写入性能等。
3.15 日志无锁刷新与多级流水
在写事务型负载中日志落盘位于性能关键路径,为了确保数据可靠性在执行INSERT、DELETE、UPDATE等操作均需要记录。经过在多核环境上的性能测试,我们发现日志在Flush时存在大量的等待,其本质原因是当前在并发场景下日志落盘环节中很难在WalInsertLock数量和锁遍历开销中取得平衡最优解,导致成为瓶颈。
上图展示了常见的日志的实现方案,主要可以归纳成3个方面:
(1)数据库内核线程必须获取日志插入锁WALInsertLock才能进入第一个临界区,在临界区中,Backend线程首先会预留WAL的插入位置,然后将生成的WAL复制到WAL Buffer的对应预留位置中。由于是多个数据库后台线程进行的并发拷贝,每个Backend线程拷贝完成的时机并不一致。
(2)数据库后台线程需要遍历所有的WALInsertLock检查lsn是否已经下盘,当WALInsertLock的数目越多时,在执行WAL Flush之前等待其他Backend线程将日志拷贝完成的时间就越长。
(3)WALInsertLock的数目越少时,XLog插入锁的抢占就越激烈。所以不管WALInsertLock的数量如何变化,系统的性能都很难达到最优,这成为了目前日志落盘的瓶颈。
GaussDB针对WalInsertLock日志锁进行优化,利用LSN(Log Sequence Number)及LRC(LogRecord Count)记录了每个backend的拷贝进度,取消WalInsertLock机制。在backend将日志拷贝至WalBuffer时,不用对WalInsertLock进行争抢,可直接进行日志拷贝操作。并利用专用的WalWriter写日志线程,不需要backend线程自身来保证xLog的Flush。通过以上优化,取消WalInsertLock争抢及WalWriter专用磁盘写入线程,在保持原有xLog功能不变的基础上,可进一步提升系统性能。针对Ustore Inplace update WAL log写入,Ustore DML operation并行回放分发进行优化。通过利用prefix和suffix来减少update WAL log的写入。通过把回放线程分多个类型,解决Ustore DML WAL大多都是多页面回放问题。同时把Ustore的数据页面回放按照blkno去分发,更好的提高并行回放的并行程度。
Ustore存储引擎
...
4 高斯数据库性能优化总结
高斯数据库GaussDB华为公司过去10年打造的一款高性能OLTP交易型数据库产品,在迭代演进过程当中吸纳当今主流数据的技术与思路,确保架构和演进层面的领先性,在达到高性能的同时能够保证事务的强一致性,在国内泛金融领域、保险、等主要行业、公司内部ERP等有广泛的应用和成功落地实践。
首先,高斯数据库从架构层面需要保证处理能力可持续提升、可横向扩展,避免某一单点问题阻碍上限的提升,因此最初架构演进上就做过深入的考虑,因此在相同代码基础上同时具备分布式、集中式两种不同的部署形态,这里集中式部署形态决定了单节点(单分片)的性能上限,而分布式将多个数据分片结合到一起通过横向扩展提升总体上限。因此从产品架构维度上看,高斯数据库的高性能技术点可分成集中式、分布式两个优化维度。
(1)集中式性能维度,主要聚焦数据库进程内的算法实现,其目标在于将节点内有限计算资源有效最大化利用。首先,从查询执行算法的宏观维度,优化器通过查询重写、CBO/ABO代价模型确保查询整体执行步骤最优;其次,执行引擎将执行计划内的每个算子执行效率发挥出来,通过节点内SMP对称多线程并行处理技术将多核CPU资源加以利用实现处理任务时序在时间维度重叠达到提升,通过算子Vec向量化、CodeGen/LLVM化等技术实现局部编译器执行,确保了CPU指令集微观层的优化效果,此外集中式场景还考虑单节点大容量数据场景,执行层面提供了丰富可选数据分区策略(Range、List、Hash、Interval、二级组合分区)能够根据用户业务的定义对数据查询范围进行数量级的裁剪,通过ustore存储引擎确保读写混合负载长稳性能以及空间节省,同时为了保证数据的强一致性处理WAL日志落盘也进行了异步流水化改造提升单位时间内日志持久化落盘的速率,进而提升读写事务的性能。再次,在性能稳定性层面,同样做了线程池化、内存共享化改造能够确保并发线程在内存、CPU调度之间得到平衡,能够在极致并发场景下如5x(相对满负荷负载并发)并发性能不显著下降,十倍甚至百倍并发系统不崩溃,并已在公司MetaERP项目中得到过实战检验。
(2)分布式性能维度,主要聚焦多数据分片汇聚线性度确保性能可横向扩展,在高斯数据库里实现了CN轻量化技术、分布式单分片事务降低CN路由和本地事务处理开销能够在256大集群内线性度达0.85以上,对于分布式事务提供了轻量化全局事务GTM-lite技术能够让分布式事务开销进一步降低,在标准TP点查负载模型下32节点以内线性度达到0.9以上。分布式大数据量处理处理场景下,分布式优化器能够基于全据统计信息合理生成分布式查询计划确保在数据搬迁-本地计算量两个维度获得最优解,实现跑批处理的高效性,分布式执行框架串联多个分片计算单元在分片间实现并行计算,实现节点内-节点间的双重并行叠加最大化分布式系统的资源利用率,在数据分布层面同样提供HASH、LIST、RANGE分布策略供客户应用场景进行选择,实现了分布-集中式分区两个维度的数据分片能力叠加,极大提升了大数据量场景下的数据剪枝优化能力,并在CBG、邮储等核心业务系统中得到实战检验
其次,为了确保高斯数据库性能易用性,针对历史版本升级、异常场景下的逃生策略同样做了一些列性能辅助特性,例如SPM计划管理、SQL-PATCH能够在版本升级、系统迁移、统计信息突变场景下对已有optimal计划起到保护作用,避免计划跳变导致的性能劣化,基于线程调度抗过载逃生能有效控制慢查询等"性能炸弹"进行有效隔离,在企业应用的一些边界极端场景确保数据库性能SLA达成。
回顾过去,GaussDB在演进迭代过程中积极吸取了时下关系型数据库、分布式数据库的设计与优化方案,在短短的10年内走完了A国友商40年的演进历程,同时并未对分布式TP/AP系统的复杂性妥协,在确保单节点性能领先的前提下仍然可通过分布式提升整体的性能上限并具备0.85+不错线性扩展比。放眼未来,随着计算机软硬件、OS/编译器/网络协议等基础软件不断革新,高斯数据库性能优化也扎根到与之相结合的新高度,在近年出现的多核128/256多核CPU,鲲鹏/x86体系结构相关背景下基于多核NUMA的优化方案也在产品里落地商用,其中鲲鹏2/4路180w/230w tpmC、32节点1600w tpmC的优势更是在友商性能性能比拼测试中获胜,此外还进行编译器相结合的优化,基于毕昇编译器的PGO、LTO、BOLT、静态编译优化等手段已在局点实战比拼中提升已有上限的20-30%,进一步巩固领先优势并在可预见的将来落地商用版本。
欢迎小伙伴们交流~