华为GaussDB相比PostgreSQL做了哪些内核优化?

  • 2019 年 12 月 18 日
  • 筆記

行业背景

随着全球经贸摩擦与中美贸易战愈演愈烈,国家基础软件自主可控被提上议程。数据库作为基础设施中重要的一环,承担着不可忽视的作用。近几年国产数据库得到了飞速的发展,特别是云数据库、分布式数据库产品也越来越多。这其中华为公司也已经在数据库领域深耕多年。

华为公司在今年5月份推出了自研的AI Native数据库GaussDB。GaussDB分为三个产品线,GaussDB100、GaussDB200、GaussDB300。

GaussDB100是华为和招行合作研发的纯OLTP类数据库,底层基于华为自研的GMDB内存数据库,GMDB是华为最早自主研发的内存数据库,主要应用于电信相关领域,与招行合作研发的GaussDB100也已经在招行若干系统上线。

GaussDB200是华为与工行合作研发的纯OLAP类数据库,GaussDB200基于经典pgxc架构,底层基于postgresql9.2版本研发,是一款分布式mpp数据库,目前已经在工行有大规模应用。

GaussDB300是基于GaussDB200演变而来,在原有集群上做了很多OLTP的适配,是一款HTAP混合负载分布式数据库,当然现在GaussDB300也有单机版,目前300也在和国内某大行进行合作研发。本文主要介绍一下GaussDB300数据库架构演进以及与pg对比高斯在内核层面做了哪些优化。

架构演进

相比postgresql-xc架构,GaussDB200分布式数据库新增了集群管理cmserver作为集群的大脑进行集群调度管理,增加cmagent作为集群每个节点上的agent来完成cmserver下发的具体任务,增加ommonitor监控组件监控每个节点上相关进程状态,实现进程故障自愈等功能。集群管理功能比较强大,目前实现了集群节点故障或者亚健康的自动切换,故障场景涵盖面比较全,比如宕机、数据库进程异常终止、网卡故障、文件系统只读、磁盘缓慢等场景都能够触发自动切换。总体架构如下:

GaussDB300数据库在200的基础上新增了etcd集群,用于保存gtm的全局事务id号,同时数据节点由原来的主-备-从(从节点不含数据,当主节点宕机切换后,从节点才有数据)变为现在的一主多备模式,每个备节点都有数据,节点间使用quorum协议。Gtm集群由原来的一主一备变为了一主多备。总体逻辑架构如下 :

整个系统采用多租户架构,应用通过负载均衡lvs或者f5连接到多个协调节点(CN),协调节点是无状态的,协调节点将应用的sql进行解析,生成分布式执行计划下发给数据节点(DN),在开启事务时,CN会先去全局事务管理器(GTM)集群取事务号和快照信息,最新版本的集群已经将事务号存储在etcd集群中,etcd是集群raft协议的分布式存储。

内核优化

1.进程模型改为线程模型

我们知道postgresql是进程模型,各个后台进程由postmaster主进程创建,每个客户端连接进来服务器端都会创建一个server process与客户端交互,处理客户端请求。进程模型有个问题就是容易造成oom,当爆发连接风暴时,work_mem这类参数的设置可能造成内存溢出。高斯数据库将pg的进程模型改为了线程模型,不仅包括server process,也包括各类后台进程,这样提高了内存信息的安全性,也降低了内存溢出的风险。但是这样修改其实有利有弊,不好的地方就是,如果发生遇到使用pg_terminate_backend(pid)杀不掉某条sql的情况下(笔者生产环境多次遇到),pg可以在操作系统层面使用kill命令杀掉该连接,而高斯则不能,因为如果使用kill杀掉,会造成数据库主进程也被kill。

2.高可用架构增强

传统pgxc架构下,高可用其实并不完善,虽然支持多写,其实也是协调节点层面的多写,数据节点还是一份,gtm这块也是单节点或者主备架构。GaussDB300在高可用这个层面做了很多优化,比如gtm改为一主多备架构,主gtm宕机能够自动切换到备gtm,同时引入主备超时机制,在早期的版本中,因为主备之间要同步gxid信息,所以当备gtm宕机时会影响主的运行,进而造成整个集群hang,这块经过优化后gtm主备之间超时会自动变为异步。数据节点层面通过shareding key的哈希策略将数据分散到每个dn分片,每个dn分片由原来的单节点变为每个分片的一主多备模式,使用quorum协议进行复制,少部分数据节点的宕机不影响全局可用性。同时新增了非常完善的监控及集群管理组件,如果某个进程异常停止,监控会立即报告集群管理将进程尝试拉起,无法启动则会进行切换。集群管理除了控制切换这块十分完善,还有个很强大的功能,主备切换后如果原主库需要rebuild也是集群自动完成的,集群管理会判断原主再加入集群时需不需要执行pg_rewind,如果不需要则自动加入,如果需要,集群管理会自动进行增量或者全量的rebuild操作,这个功能是很好的,考虑的十分全面。

3.使用etcd集群存储全局事务号

Gtm组件是集群的心脏,每个事务的开启都需要cn与gtm的交互,需要取gxid和快照信息,所以在高并发的情况下交互十分频繁,所以gtm的网络流量是非常大的,gtm可能来不及分配事务号,很容易成为瓶颈。同时,gtm主备节点之间同步信息会造成高可用的问题,任何一个事务号都要到备节点进行sync操作,如果发生某个gtm节点宕机,会造成超时等待,整个集群会hang,造成高可用的问题。针对上面两个问题,GaussDB300引入了第三方etcd集群,etcd集群是基于raft协议的高可用强一致集群,最少三个节点,比较轻量化,十分适合用来存储gxid,这样一来,gtm的工作就剥离一部分到etcd了,高可用就依赖于etcd集群的成熟高可用方案,大大缩短了gtm宕机对集群的影响。Gtm就只负责从etcd集群读写事务号,这块设计十分巧妙。

4.XID事务号从32位改为64位

可能大家看到这条的时候可能心里震撼了一下。32位的事务id是pg永远的痛,也许以前pg更多的用于分析类场景,所以事务号的消耗没有那么大,但是现在高并发场景越来越常见,32位事务id很快就会被消耗殆尽,32位事务号能容纳的最大事务号是42亿左右。经过测试,pg每秒最多插入大概100000条数据,按照1秒消耗十万事务号来计算,42亿事务号大概只能支撑12个小时,事务号是循环使用的,所以其实用到一半21亿的时候为了保证事务可见性就需要做freeze操作,freeze操作会对超过年龄的表进行冻结,冻结操作(也被称为冻结炸弹)会拒绝所有数据库连接,必须进入单用户模式执行vacuum freeze操作,这对数据库无疑是非常难以接受的。华为将事务号改为64位后很明显已经达到了永远也用不完的情况。当然这块改动也是很大的,很多其他模块也需要做联动,比如提交日志clog这块的寻址和清理也需要做相应的调整。

5.GTM性能增强

这个涉及到两方面问题:第一方面gtm分配gxid的速度成为瓶颈,针对这方面优化为使用etcd作为gxid的存储,同时gxid的分配由原来的一次分配一个事务号改为一次批量分配200000事务号,事务开启后按需去拿已经分配好的序号就行,这样大大减轻了gtm事务号分配的压力。第二方面的问题是高并发下cn与gtm的交互过多,网络流量过大,针对这个问题华为也已经在预研gtm-lite,对于非全局类型的事务,cn就只作为一个转发者,将sql下发,而不再去gtm上取快照信息,oceanbase现在使用的就是这个方案,华为还在研发。

6.流复制增强

我们知道postgresql需要通过pacemaker+corosync或者repmgr这类的外部工具实现pg的高可用切换方案,华为抛弃了原来pg配置recovery.conf的方式配置流复制,转而直接将连接信息放在postgresql.conf文件中,并且将高可用方案做到了内核层面,而不是依赖于外部工具(当然华为也会做一套高可用集群管理工具)。现在单机版GaussDB在不依赖于集群管理的情况下,一主多备能够实现主备切换后备库自动连接到新主库,还有主备切换后原主库以备库的角色重新加入集群会自动判断是否需要做rebuild(pg里是pg_rewind)操作,如果不需要,则自动加入,如果需要就会自动做rebuild操作,然后加入集群,很多以前pg里涉及切换需要手动操作的内容,在gauss里已经在内核层面实现自动化,而且不会造成数据丢失。

上面都是比较大的改动,除了上面的优化还有很多细节上的改动,比如集群日志优化、监控视图优化、安装部署优化、时间线timeline优化等。

当然华为做的这些内核改动也是一把双刃剑,有得有失,但是总体利远远大于弊。我觉得最大的弊端就是失去了插件的支持,pg是一个非常干净的开源数据库,体积很小,生态很好,很多优秀的功能或者运维工具都可以通过扩展插件的方式来进行支持,我们知道pg有很多优秀的插件,比如:pg_stat_statment、pg_repack、bloom、citus、repmgr、pg_strom、pg_pathman等。还有一个问题就是数据库体积过于庞大,很多无用的文件,相比pg显得比较臃肿。