本文作者是 OceanBase创始人阳振坤、OceanBase技术专家曹晖、OceanBase资深技术专家陈萌萌、OceanBase资深技术专家潘毅、OceanBase资深技术专家韩富晟、OceanBase高级技术专家赵裕众。本文原载于阿里技术微信公众号(ali_tech),AI开发者获授权转载。
前言
蚂蚁金服自主研发的金融级分布式关系数据库 OceanBase,不久前在被誉为“数据库领域世界杯”的 TPC-C 基准测试中打破了由美国公司 Oracle(甲骨文)保持了 9 年之久的世界记录,成为首个登顶该榜单的中国数据库产品。
很多人都好奇 OceanBase 究竟有何能耐取得这个结果,这篇文章也许能够告诉我们答案。
阿里妹导读:TPC-C是TPC组织(国际事务性能委员会)制定的关于商品销售的订单创建和订单支付等的基准测试标准,是数据库联机交易处理系统的权威基准测试标准。
蚂蚁金服自研的分布式关系数据库OceanBase获得TPC-C测试第一名后,引起了大量关注,今天,我们邀请了OceanBase的核心研发人员对本次测试做专业的技术解读。
一、OceanBase如何做TPC-C测试
有机会挑战TPC-C测试相信是所有数据库内核开发人员的梦想,但TPC-C测试标准非常复杂。由于这是国产数据库同时也是分布式数据库第一次冲击这个榜单,为了完成这次挑战,OceanBase团队前后准备时间超过一年。
前期准备
TPC-C测试首先需要找到官方唯一认证的审计员来对测试进行审计监察,他们对这次OceanBase的审计也相当重视,全世界仅有的三个审计员这次就有两个参与到测试审计工作中。
测试系统
目前市面上基本找不到一个能够开箱即用的符合TPC-C标准的测试工具。以目前各个厂商PoC环境最常遇到的benchmarksql为例,可以说只是模拟TPC-C压力模型的压测工具,连最基本的数据导入都不合规,大量的字符串生成未保证全局随机,缺乏压测阶段最基本的think time、keying time这些基本配置导致极小的数据量就能跑出很高的tpmC,最关键的是它将测试模型大大简化为工具直连DB测试,完全没有遵守TPC-C测试标准规范。
在标准定义中,测试系统可以分为RTE(Remote Terminal Emulator)和SUT两部分,但实际上从角色上看SUT可以进一步拆分为两部分:WAS(web application server)和DB Server。
这其中DB Server是每个测试厂商的数据库服务;RTE扮演的角色是测试模型中的客户终端,事务的触发、RT的统计等都在这里完成;标准明确要求每一个用户terminal都必须保持一个长连接,显然在海量Warehouse时DB是无法承受这么多连接的,WAS就是RTE和DB之间桥梁,标准定义可以使用连接池,在保证对应用透明的情况下它可以做所有请求的管理。
这三个角色中,WAS和DB是必须商业化可购买且提供支付服务的,OceanBase这次是使用了OpenResty作为WAS供应商。而RTE则一般由各个参测厂商自行根据标准实现,但所有实现代码必须经过审计的严格审计,OceanBase这次完整的实现了一整套完全合规的RTE,并且支持在大规模测试系统中部署。要知道在实际的TPC-C测试流程中,不只是对DB端能力的考验,RTE端同样存在极大的资源消耗和压力。以这次6088w tpmC测试结果看,我们一共在64台64c128G的云服务器上运行了960个RTE客户端,来模拟总计47942400个用户terminal,最后还需要基于这么多RTE统计结果进行一致性和持久化审计验证。
虽然只是测试客户端,但RTE的中同样有大量的可能导致最后测试失败的小细节,比如大家可能注意不到的所有事务都因为用了web端模拟终端后需要增加的100毫秒rt,又比如为了模拟用户终端输出显示的100毫秒延时。
测试规划
TPC-C从来都不是一个简单的测试,它很科学并没有给出强制的软硬件配置,只是给出测试规范和各种审计检查限制标准,所有数据库厂商可以根据自己的特性充分调优来拿到一个最好的性能或性价比。但这同时也对所有参测厂商提出了一个巨大的难题,如何对已有的资源进行合理规划来保证顺利完成一次对TPC-C榜单的冲击。
硬件选型,这里不仅要对数据库服务器选型,还需要对RTE以及WAS服务器选型。这之前需要先期进行大量的测试和调优,来摸出在保证性价比的前提下每个角色服务器的资源配置是多少刚好。这次OceanBase测试最大的优势就是全部使用了云化资源,我们不需要再关注最底层机房、机柜、布线这些细节,只需要通过快速的规格调整来拿到我们需要的机型。
参数选择,如何选择合适的配置参数是一个非常令人头疼的问题。举个例子,一个最典型的问题就是我们最终要跑多少个Warehouse,每个数据库服务器上承载多少Warehouse。TPC-C标准为了尽可能模拟真实业务场景,通过每个事务限定不同的think time和keying time保证了一个warehouse的数据最多能够提供12.86tpmC值,因此数据库厂商想要拿到更高的成绩就必须装载更多的warehouse,但是另一方面单机的存储空间又有预计80%(经验值)需要预留给60天增量存储。在分布式数据库架构下,为了能让每个数据库服务器跑满又能充分利用本地存储空间,让每个服务器的CPU、内存、IO能力、存储空间的资源最大化利用,我们前后调整优化了近一个月时间。
性能压测
最受关注的性能压测部分在TPC-C标准中规定了以下三个状态:
Ramp-up,标准允许每个数据库进行一定时间的预热来达到稳定状态,但是ramp-up阶段的所有配置必须和最终报告配置保持一致。
Steady state,保证ACID及可串行化隔离级别前提下,数据库需要能够以最终报告tpmC值在稳定状态无任何人工干预前提下保持运行8小时以上,每隔半小时需要完成一次checkpoint。
Measurement Interval,标准规定了需要能够支持8小时稳定运行,但性能采集阶段只需要保设置2小时以上即可。这个阶段还需要保证累计tpmC波动不能超过2%,并且必须完成至少4个以上的checkpoint。
所以之前一般数据库进行性能压测一般是以下的流程,先进行一段时间的预热到达稳态,等待稳定运行一段时间并完成一个checkpoint后开始进入2小时的性能采集阶段。
而OceanBase这次是使用了TPC-C测试迄今以来最严苛的一个流程来完成这个性能测试的,我们首先使用了10分钟进行预热,然后在6088w tpmC稳态保持运行25分钟并完成一个检查点,再继续跑了完整的8小时性能压测采集阶段,总耗时超过8个半小时,中间性能最大波动不到0.5%,最终结果也让审计员异常兴奋。
整个性能测试前后,审计员还需要进行数据及事务随机分布检查,简单说来就是大量全表扫描和统计sql,最大的一条sql需要访问超过万亿行的order_line表结果,可以算是TPC-C里的“TPC-H测试”。现场审计第一次遇到这些sql时我们也大量的出现sql执行超时情况,但后续基于OceanBase2.2版本最新的并行执行框架我们还是很快搞定了这些大sql,所以要顺利完成TPC-C测试并不能只是一个偏科生,保持自身没有短板才是真正意义上的通用关系数据库,从这点上说Oracle仍是OceanBase学习的榜样。
ACID
A测试,通过提交和回滚payment事务来确认数据库对原子性支持,和I测试一样,OceanBase的A测试跑了两遍,分别应对分布式事务和本地事务。
C测试,标准里的C测试一共包含12个case,前四个是必须要完成验证的,每个case其实都可以认为是一个复杂的大sql,而对于分布式数据库来说重点是需要始终保证全局一致。
I测试,标准要求TPC-C模型里5个事务除了StockLevel事务都需要满足最高的可串行化隔离级别,并构造了9个case来验证隔离性。对于分布式数据库而言,这个要求并没有那么容易实现,所幸OceanBase在2.x版本中提供了全局时间戳的支持,所以的I测试都在审计员的特别要求下跑完了全本地和全分布式两种模式的两轮测试,这也应该是TPC-C测试中首次有数据库厂商需要做两轮I测试跑18个case的,也许在不久后TPC-C标准定义也会因为OceanBase的这次测试而带来针对分布式数据库的相关更新。
D测试,OceanBase在这个场景其实相对传统数据库是有较大天生优势的,OceanBase每个Warehouse数据有两份数据三份日志,通过paxos强同步,保证RPO=0的前提下只有秒级RTO。
面对D测试标准中最严格的一项-部分存储介质永久故障,OceanBase还使用了最严苛的测试场景,在使用超出标准要求的全量6000W tpmC压力下,我们强行销毁了一个云服务器节点,整体tpmC在两分钟内恢复到6000w并持续跑到测试时间结束,这些表现都是远远超过TPC-C规范要求的,相比较而言其它传统数据库基本面对有日志的存储介质故障D测试场景都是依赖磁盘RAID来恢复,OceanBase应该算是首个没有raid完全依赖数据库自身恢复机制完成全部D测试的数据库厂商了。
同时我们在D测试中是连续杀掉了两台服务器节点,首先杀掉一个数据节点,等待tpmC恢复且稳定5分钟后,再次杀掉了rootserver leader节点,tpmC仍然快速恢复。
二、TPC-C基准测试之SQL优化
对TPC-C有所了解人都知道,TPC-C是一个典型的OLTP (On-Line Transaction Processing) 场景测试,考察的是数据库在高并发压力场景下的事务处理能力,最终的性能指标以tpmC(transaction per minute,也即每分钟系统处理TPC-C模型中的new order事务的数量)和平均到每tpmC的系统成本作为衡量标准。在OLTP场景中,每条请求的响应时间都是极短的。因此,各个数据库厂商在进行TPC-C测试时,都会尽一切可能将每一个操作时间压缩到最短,不夸张的说,在TPC-C的测试中,一些关键操作的优化往往需要细化到CPU指令级。
在进入我们的主题前,我们先来谈谈TPC-C中的事务模型,主要分为五种事务,订单创建、订单支付、订单查询、订单发货以及库存查询,这五种事务按照一定的比例发生,测试最终衡量的是每分钟订单创建事务的执行个数。大家知道,每一个数据库的事务,其实就是由一定逻辑关系关联的若干条SQL语句组成,他们在一个事务中,要么全部成功,要么全部失败,这个在数据库中称为“原子性”,也就是ACID中的“A”。那么TPC-C中的一个事务的耗时大约是多久呢?看一下报告就很清楚了——只有十几个毫秒。考虑到一个事务由多条SQL构成,那么每一条SQL的平均耗时都不到1毫秒!
在C/S(client-server)模型中,一条SQL语句从发起到执行完成需要经历从客户端输入、网络传输、SQL优化、执行、结果返回到客户端这样一个流程。而具体每一条SQL的执行可能只是做一个字段的更新,所需要的执行时间是非常短暂的,从整个链路的角度来看,大量的时间会花费在与客户端的交互过程中,造成资源的浪费和耗时的增加。那么如何解决这个问题的呢?答案就是使用存储过程。
存储过程
所谓“存储过程”就是数据库为用户提供的一种面向过程的编程语言。基于这种语言,用户可以将应用程序的逻辑封装为一个可调用的过程(procedure)存放在数据库中并随时进行调用。通过这种方式,用户可以将本来需要与数据库进行多次交互才能完成的工作通过一次交互完成,省去了中间网络的传输和等待时间(参见图1)。假如一条事务的网络开销平均是30%,也就是说30%的CPU都花在了网络的收发和解析上。那么在6千万规模tpmC测试中节省下来30%的CPU资源换算成系统处理能力是惊人的。使用存储过程还可以带来事务响应时间的下降,导致数据库内核中事务锁的临界区缩短,间接的提升了系统CPU利用率,整个吞吐量也随之提高。存储过程在缩短应用端的等待耗时上同样有很大作用。
图1 传统的C/S模型与使用存储过程的执行方式对比
在TPC-C中,存储过程对于整个系统的执行效率提升是至关重要的。OceanBase 的2.2版本不仅全面支持了存储过程,而且对存储过程的执行效率做了大量极致的优化。
编译执行
存储过程作为一种面向过程的高级语言,需要转换成机器码才能够执行。这个过程一般可以分为“编译执行”和“解释执行”两种,一般来说,编译执行相比解释执行有代码优化充分、执行效率高等特点。OceanBase利用近两年逐渐成熟的LLVM编译器框架实现了一个支持存储过程的编译器,通过动态编译(Just-in-Time Compilation)的方式将存储过程翻译成高效的二进制可执行代码,在执行效率上获得了数量级的提升。同时,过程中LLVM框架将存储过程转换为与机器无关的中间代码,使得存储过程也自然而然地获得了跨平台的编译执行能力,LLVM内置的优化过程确保我们在各种不同的硬件平台上都可以获得正确、高效的可执行代码。
Array Binding
另外一个在TPC-C测试中发挥了重要作用的功能就是对DML语句进行批量处理的能力,在Oracle中该功能也称为“Array Binding”。一条SQL在数据库中的执行过程大致上可以分为“计划生成”和“执行”两个阶段。尽管我们对SQL的执行计划做了高速缓存,但找到一个合适的执行计划在整个执行过程中仍然是比较耗时的一个部分。那有没有办法省去这个时间呢?当一组SQL的执行计划完全一样而只有执行参数不同时,在存储过程中我们可以通过特定的语法将他们的执行做成一个批量处理的过程,此时“计划生成”只需要做一次即可,这就是所谓的“Array Binding”。
在Array Binding中,数据库会首先找到需要使用的计划,然后执行该计划,并在每次执行完毕后,重新执行参数绑定(binding)的过程。打个比方,这就像是在一个C语言的for循环中,反复赋值而不是重新定义一个数据结构。Array Binding的使用受用户控制,需要在存储过程中使用FORALL关键字来触发这一功能,在TPC-C的测试过程中,我们多次使用了Array Binding来提升系统的处理能力,效果非常明显。
Prepared Statement与执行计划缓存
Prepared Statement是一种二进制的请求交互协议,可以大大降低系统的交互成本。OceanBase不仅支持用户程序与数据库间使用Prepared Statement, 也支持在存储过程引擎调用SQL引擎执行时使用这种交互方式。存储过程在对SQL进行一次Prepare操作并获取唯一id后, 后续的每次执行仅需要传入该id和对应的参数,系统可以通过高速缓存找到对应的存储过程或SQL计划开始执行。该过程相比使用SQL文本的交互方式,省去了大量请求文本解析的CPU开销。
OceanBase内部实现了高速缓存来缓存存储过程的可执行代码及SQL执行计划,不同参数的存储过程和SQL可以通过这一高速缓存快速获取需要的执行对象, 耗时一般在几十微秒以内, 有效避免了重新编译带来的毫秒级的延迟和CPU消耗。
可更新视图
在OLTP场景中,通过减少应用与数据库的交互次数来实现性能提升的例子很多,可更新视图就是其中之一。我们常见的数据库视图通常是只读的,通过定义视图,用户可以定义自己感兴趣的数据以及其获取接口,但视图同时也可以作为更新操作的入口,比如在TPC-C的new order创建场景中,应用需要得到商品信息,更新库存并得到更新后的值。一般可以通过两条SQL实现这一过程:
select i_price,i_name, i_data from item where i_id = ?;
UPDATE stock
SET s_order_cnt = s_order_cnt + 1,
s_ytd = s_ytd + ?,
s_remote_cnt = s_remote_cnt + ?,
s_quantity = (CASE WHEN s_quantity< ? + 10 THEN s_quantity + 91 ELSE s_quantity END) - ?
WHERE s_i_id = ?
AND s_w_id = ?
RETURNING s_quantity, s_dist_01,
CASE WHEN i_data NOT LIKE'%ORIGINAL%' THEN 'G' ELSE
(
CASE WHEN s_data NOT LIKE '%ORIGINAL%' THEN 'G'ELSE 'B' END
) END
BULK COLLECT INTO ...
但通过建立一个可更新视图:
CREATE VIEW stock_item AS
SELECT i_price, i_name, i_data, s_i_id,s_w_id, s_order_cnt, s_ytd, s_remote_cnt, s_quantity, s_data, s_dist_01
FROM stock s, item i
WHERE s.s_i_id =i.i_id;
我们就可以通过一条语句更新库存并得到商品和库存信息:
UPDATE stock_item
SET s_order_cnt = s_order_cnt + 1,
s_ytd = s_ytd + ?,
s_remote_cnt = s_remote_cnt + ?,
s_quantity = (CASE WHEN s_quantity< ? + 10 THEN s_quantity + 91 ELSE s_quantity END) - ?
WHERE s_i_id = ?
AND s_w_id = ?
RETURNING i_price, i_name, s_quantity,s_dist_01,
CASE WHEN i_data NOT LIKE'%ORIGINAL%' THEN 'G' ELSE (CASE WHEN s_data NOT LIKE '%ORIGINAL%' THEN 'G'ELSE 'B' END) END
BULK COLLECT INTO ...;
这样就省去了一条语句的交互,并且更新逻辑更加直观。可更新视图允许用户可以像普通表一样操作视图,但不是所有视图都可以定义为可更新视图。比如带distinct, group by的视图,具体更新哪些行语义是不明确的,因此不能允许更新。具体到上面的stock_item两表join的视图,需要满足所更新表的unique key在join之后保持unique(key-preserved table),即item.i_id必须是唯一的这个前提。
需要强调,TPC-C规范禁止使用物化视图,而可更新视图并没有改变底层数据表格的存储形式,是符合规范的。
因为TPC-C的设计原则是尽可能的“真实”反应一个OLTP系统的运行场景,我们所做的很多优化都具有广泛的适用性。例如,对于一个高并发的OLTP系统来说,大部分的SQL请求的耗时是非常短的,采用纯粹的C/S交互模型的后果必然使系统的时间浪费在应用与数据库的频繁交互中,而使用存储过程可以大大缓解这种交互的耗时,并且增强系统对于网络抖动的免疫力,这种核心能力对于一个分布式OLTP数据库是不可或缺的。
在这次的TPC-C测试中,我们采用了OceanBase 2.0版本开始支持的Oracle兼容模式,存储过程和SQL全部使用了兼容Oracle的数据类型和语法,这样做也是为了在追求极致优化的同时,确保产品迭代可以沿着通用和正规的方向发展。
三、TPC-C基准测试之数据库事务引擎的挑战
OceanBase这次TPC-C测试与榜单上Oracle和DB2等其他数据库在硬件使用上有非常大的不同,OceanBase的数据库服务器使用的是204+3台型号是ecs.i2.16xlarge阿里云ECS服务器,其中204台作为data node,还有3台作为root node,每位读者都可以在阿里云网站上轻松按需购买。如果读者翻看Oracle和DB2的TPC-C测试报告会发现,这些数据库都会使用专用的存储设备,例如前最高记录保持者Oracle在2010年的测试,使用了97台COMSTAR专用的存储设备,其中28台用来存储数据库的重做日志(Redo Log)。
硬件的差异给软件架构提出了完全不同的挑战,专用的存储设备其内部通过硬件冗余实现了设备自身的可靠保证,数据库软件在使用这样的存储设备时就天然的预设了数据不会丢失。但是,这种方式带来了成本的极大消耗,专用的存储设备的价格都是特别昂贵的。
OceanBase使用通用的ECS服务器提供数据库服务,并且只使用ECS机器自带的本地硬盘做数据存储,这是最通用的硬件条件。但是这种方式对软件架构提出了很大的挑战,因为单个ECS服务器的不如专用的存储设备可靠性高。这也对OceanBase的事务引擎提出了很大的挑战,OceanBase是在普通的ECS服务器上就可以实现ACID特性。
TPC-C测试是对事务ACID特性有完整并且严格的要求。下面分别介绍OceanBase针对事务ACID的特性的解决方案。
Paxos日志同步保证持久性(Durability)
OceanBase数据库的事务持久性(Durability)保证是依赖事务重做日志(Redo Log)的持久性来达成的。所有的 Redo Log 会实时强同步到另外两台数据库服务机器上,包含产生 Redo Log 的机器在内,总共会有三台机器在硬盘中持久化 Redo Log。
OceanBase 采用了 Paxos 一致性同步协议来协调这三台机器上 Redo Log 的持久化,Paxos协议采用超过半数(也叫“多数派”)成功即算成功的算法(三个副本时,两个成功即超过半数),当其中两台机器完成持久化后,事务即可完成提交,剩下的一台机器的 Redo Log 在通常情况下,也是立即就持久化完成了。但如果这台机器碰巧出现异常,也不会影响事务的提交,系统会在其恢复后自动补齐所缺失的 Redo Log。如果机器永久故障,系统会将故障机器所应负责同步的数据分散给集群内的其他机器,这些机器会自动补齐所缺失内容,并跟上最新的 Redo Log 写入。
使用Paxos一致性协议的最大优势是数据持久化和数据库服务可用性的完美平衡。当使用三个副本时,任何时候坏掉一个副本时至少还有另一个副本有数据,并且写入还可以持续,因为还剩下两个副本,后续的写入也不受影响。
所以,OceanBase 在保证了事务持久性的同时,也大大提升了数据库的连续服务能力。TPC组织的审计员在现场审计OceanBase持久性能力时,在客户端持续产生压力的情况下,从OceanBase集群中随意挑选了一台机器做了强制断电操作,发现数据库的数据不仅没丢,数据库不需要任何人工干预还能持续的提供服务,审计员们都很吃惊,并且对OceanBase大为赞赏。
依靠自动两阶段提交解决原子性(Atomicity)
TPC-C测试模型的五种事务中的“订单创建”和“订单支付”两个事务分别会对很多数据做修改,是其中相对复杂的两个事务。TPC-C标准对事务的原子性(Atomicity)是强制性的要求,要求一个事务内部对仓库、订单、用户等表格的修改一定要原子的生效,不允许出现只有一半成功的情况。
OceanBase的数据是按照仓库ID(Warehouse_ID)拆分到多台机器上的,如果所有的事务都是发生在同一个仓库内部,那么无论数据量有多大,事务的修改都只会涉及一台机器的数据,也就是在一台机器上完成事务提交,这是一种完美的线形扩展的场景。但是这不符合实际的业务场景,大多数的实际业务都会有很多不同维度之间的数据交互。TPC-C测试标准也是对此认真考虑,所以对于事务操作数据的随机性规则提出了要求,最终要保证产生10%的“订单创建”事务和15%的“订单支付”事务要操作两个及以上的仓库。在OceanBase数据库内,这样就产生了跨机器的事务操作,而这必须使用两阶段提交协议来保证原子性。
OceanBase会自动跟踪一个事务内所有SQL语句操作的数据,根据实际数据修改的位置自动确定两阶段提交的参与者,事务开始提交时,OceanBase自动选择第一个参与者作为协调者,协调者会给所有参与者发送Prepare消息,每个参与者都需要写各自的Redo Log和Prepare Log(也意味着每个参与者各自做自己的Paxos同步),等协调者确认所有参与者的Redo Log和Prepare Log完成后,然后再给所有参与者发送Commit消息,再等所有参与者的Commit工作完成。整个协议是在事务提交过程中自动完成,对用户完全透明。OceanBase为每一个两阶段提交事务自动选择一个协调者,整个系统任何机器都可以分担协调者工作,所以OceanBase可以将事务处理能力进行线形扩展。
多版本并发控制保证事务的隔离性(Isolation)
TPC-C标准里要求“订单创建”、“订单支付”、“订单配送”、“订单支付”事务之间都是串行化隔离级别(Serializable)。OceanBase采用的方法是基于多版本的并发控制机制。事务提交时会申请一个事务的提交时间戳,事务内的修改以新的版本写入存储引擎,并且保证之前版本的数据不受影响。事务开始时会获取一个读取时间戳,整个事务内数据的读取操作只会看到基于读取时间戳的已提交数据。所以,事务的读取不会遇到脏数据、不可重复读数据以及幻读数据。同时,事务的修改会在修改的数据行上持有行锁,保证两个并发的修改相同行的事务会互斥。
OceanBase的全局时间戳生成器也是由多副本组成,可以独立部署在三台机器上,也可以像这次TPC-C评测中一样部署在root node机器上,与root node共享资源。全局时间戳的三副本是一种极高可用的架构,任何一次时间戳的获取操作都至少在三台机器上的两台获得了确认,所以任意一台机器出现故障,获取时间戳的操作不会有一点影响。
按照TPC-C标准,OceanBase准备了9种不同的场景测试有读-读、读-写冲突时事务的隔离性,最终都完美通过了审计员的审计。
一致性保证(Consistency)
在有了上述的事务能力后,OceanBase可以完美的保证各种数据的一致性的约束。TPC-C标准里提出了12种不同的一致性测试场景在各种测试运行前后对数据库内的数据进行一致性校验。因为OceanBase此次测试数据规模庞大,一致性校验的SQL需要核对大量的数据,所以一致性校验的挑战在于校验的SQL本身运行的效率。基于OceanBase的并行查询能力,发挥整个集群所有的计算资源,校验SQL的运行时间均缩短了几个数量级,很好的完成一致性功能的审计工作。
复制表
TPC-C测试模型中有一张商品(ITEM)表,这张表的内容是测试所模拟的销售公司所有售卖的商品信息,包含了商品的名字、价格等信息。“订单创建”事务执行中需要请求这张表内的数据来确定订单的价格信息,如果商品表的数据只存放在一台机器上,那么所有机器上发生的“订单创建”事务都会请求包含商品表的机器,这台机器就会成为瓶颈。OceanBase支持复制表功能,将商品表设置为复制表后,商品表的数据会自动复制到集群中的每一台机器上。
TPC-C标准不限制数据的副本数,但是不管数据的组织形式,标准里要求事务的ACID一定要保证。OceanBase使用特殊的广播协议保证复制表的所有副本的ACID特性,当复制表发生修改时,所有的副本会同时修改。并且,当有机器出现故障时,复制表的逻辑会自动剔除无效的副本,保证数据修改过程中不会因为机器故障出现无谓的等待。复制表在很多业务场景中都有使用,例如很多业务中存储关键信息的字典表,还有金融业务中存储汇率信息的表。
四、TPC-C基准测试之存储优化
TPC-C规范要求被测数据库的性能(tpmC)与数据量成正比。TPC-C的基本数据单元是仓库(warehouse),每个仓库的数据量通常在70MB左右(与具体实现有关)。TPC-C规定每个仓库所获得的tpmC上限是12.86(假设数据库响应时间为0)。
假设某系统获得150万tpmC,大约对应12万个仓库,按照70MB/仓库计算,数据量约为8.4TB。某些厂商采用修改过的不符合审计要求的TPC-C测试,不限制单个warehouse的tpmC上限,测试几百到几千个warehouse全部装载到内存的性能,这是没有意义的,也不可能通过审计。在真实的TPC-C测试中,存储的消耗占了很大一部分。OceanBase作为第一款基于shared nothing架构登上TPC-C榜首的数据库,同时也作为第一款使用LSM Tree存储引擎架构登上TPC-C榜首的数据库,在存储架构上有如下关键点:
为了保证可靠性,OceanBase存储了两个数据副本和三个日志副本,而传统的集中式数据库测试TPC-C只存储一份数据;
由于OceanBase存储两个数据副本,再加上OceanBase TPC-C测试采用了和生产系统完全一样的阿里云服务器i2机型,SSD硬盘的存储容量成为瓶颈。OceanBase采用在线压缩的方式缓解这个问题,进一步增加了CPU使用;相应地,集中式数据库测试存储一份数据,不需要打开压缩;
OceanBase LSM引擎定期需要在后台做compaction操作,而TPC-C要求测试至少运行8小时且2小时之内抖动小于2%,因此,OceanBase存储需要解决LSM引擎后台操作导致的抖动问题;
两份数据
为了保证可靠性和不丢数据(RPO=0),有两种不同的方案:一种方案是在硬件层面容错,另一种方案是在软件层面容错。OceanBase选择在软件层面容错,优势是硬件成本更低,带来的问题是需要冗余存储多个副本的数据。OceanBase使用Paxos协议保证在单机故障下数据的强一致。在Paxos协议中,一份数据需要被同步到多数派(超过一半),才被认为是写入成功,所以一般来说副本个数总是奇数,出于成本考虑最常见的部署规格是三副本。
三副本带来的首要问题就是存储成本的上升,之前商业数据库的TPC-C测试大多基于磁盘阵列,而TPC-C规范中明确对磁盘阵列不做容灾要求,使用相对于传统数据库三倍的存储空间进行TPC-C测试显然难以接受。
我们注意到这样一个事实,通过Paxos协议同步的只是日志,日志需要写三份,但数据不是,数据只需要有两份就可以完成单机故障的容灾了,当一份数据由于服务器宕机不可用时,另一份数据只要通过日志把数据补齐,就可以继续对外提供访问。
和数据存储相比,日志的存储量比较小。我们将数据与日志分开,定义了三种不同的副本类型:F副本既包含数据又同步日志,并对外提供读写服务;D副本既包含数据又同步日志,但对外不提供读写服务;L副本只同步日志,不存储数据。当F副本出现故障时,D副本可以转换为F副本,补齐数据后对外提供服务。在TPC-C测试中我们使用FDL模式进行部署(一个F副本,一个D副本,一个L副本),使用了两倍数据副本的存储空间。无论是D副本还是L副本,都需要回放日志,D副本还需要同步数据,这些都是都会消耗网络和CPU。
在线压缩
在sharednothing架构下,OceanBase至少需要存储两份数据才可以满足容灾的要求,这意味着OceanBase需要比传统数据库多耗费一倍的存储空间。
为了缓解这个问题,OceanBaseTPC-C测试选择对数据进行在线压缩,Oracle数据库中一个warehouse的存储容量接近70MB,而OceanBase压缩后存储容量只有50MB左右,大幅降低了存储空间。TPC-C规范要求磁盘空间能够满足60天数据量的存储,对于OceanBase,由于需要保存两份数据,虽然可靠性更好,但需要保存相当于120天的数据量,这些存储成本都要计入总体价格。
OceanBase使用了204台ECS i2云服务器存储数据,服务器规格和线上真实业务应用保持一致。每台服务器的日志盘1TB,数据盘接近13TB。计算两份压缩后的数据60天的存储空间之后,服务器的数据盘基本没有太多余量,从服务器的资源成本消耗来看,已经达到了比较好的平衡。如果OceanBase的单机性能tpmC进一步提升,磁盘容量将成为瓶颈。OceanBase LSM引擎是append-only的,它的优势是没有随机修改,能够在线压缩。无论是TPC-C测试,还是最核心的OLTP生产系统(例如支付宝交易支付),OceanBase都会打开在线压缩,通过CPU换存储空间。
存储性能平滑
TPC-C测试很大的挑战在于在整个压测过程中性能曲线要求是绝对平滑的,曲线上的波动幅度不能超过2%,这对于传统数据库来说都是一件困难的事情,因为这要求对于所有后台任务的精细控制,不能由于某个后台任务的资源过度使用导致前台请求的阻塞积压。而对于OceanBase而言,事情变得更为困难,因为OceanBase的存储引擎是基于LSM Tree的,在LSM Tree要定期执行compaction操作。Compaction是个非常重的后台操作,会占用大量CPU和磁盘IO资源,这对前台的用户查询和写入天然就会造成影响。我们做了一些优化,来平滑后台任务对性能的影响,从最终的测试结果来看,性能曲线在整个8小时压测过程中的抖动小于0.5%。
★ 分层转储
在LSMTree中,数据首先被写入内存中的MemTable,在一定时候为了释放内存,MemTable中的数据需要与磁盘中的SSTable进行合并,这个过程被称为compaction。在很多基于LSM Tree的存储系统中,为了解决写入的性能问题,通常会将SSTable分为多层,当一层的SSTable个数或者大小达到某个阈值时,合并入下一层SSTable。多层SSTable解决了写入的问题,但是SSTable的个数过多,会极大拖慢查询的性能。OceanBase同样借鉴了分层的思路,但同时使用了更加灵活的compaction策略,确保SSTable总数不会太多,从而在读取和写入性能之间做了更好的平衡。
★ 资源隔离
Compaction等后台任务需要消耗大量的服务器资源,为了减少后台任务对用户查询和写入的影响,我们在CPU、内存、磁盘IO和网络IO四个方面对前后台任务做了资源隔离。在CPU方面,我们将后台任务和用户请求分为不同的线程池,并按照CPU亲和性做了隔离。在内存方面,对前后台请求做了不同的内存管理。在磁盘IO方面,我们控制后台任务IO请求的IOPS,使用deadline算法进行流控。在网络IO方面,我们将后台任务RPC和用户请求RPC分为不同队列,并对后台任务RPC的带宽使用进行流控。
★存储CPU占用
TPC-C基准测试主要考察整体性能tpmC,很多人也会关注单核的tpmC。然而,这个指标只有在相同架构下才有意义。对于存储模块的CPU占用,有如下三点:
对于集中式架构,除了数据库使用CPU之外,专用存储设备也需要使用CPU。例如,第二名Oracle 3000多万tpmC的测试中,数据库使用了108颗T3SPARC处理器,共有1728个物理核心和13824个执行线程,同时存储设备使用的是Intel服务器作为机头,总共使用了97台服务器,194颗Intel X5670 CPU,2328个物理核心。
集中式数据库使用高可靠硬件,只需要存储一个副本,而OceanBase通过软件层面容错,虽然硬件成本更低但需要两个数据副本和三个日志副本,维护多个副本需要耗费大量CPU;
OceanBase在TPC-C测试和生产系统中都打开了在线压缩,进一步增加了CPU使用;
因此,简单地对比OceanBase和Oracle的CPU核是不科学的,还需要算上共享存储设备的CPU核,以及OceanBase存储多副本和在线压缩带来的CPU开销。TPC-C推荐的方案是不关注具体的软件架构和硬件架构,关注硬件总体成本。在OceanBase的测试中,硬件成本只占整体成本的18%左右,只考虑硬件的性价比大幅优于集中式数据库。
后续发展
OceanBase的优势在于采用分布式架构,硬件成本更低,可用性更好且能够做到线性扩展,但是,OceanBase单机的性能离Oracle、DB2还有不小的差距,后续需要重点优化单机存储性能。另外,OceanBase的定位是在同一套引擎同时支持OLTP业务和OLAP业务,而目前OceanBase的OLAP处理能力还不如Oracle,后续需要加强存储模块对大查询的处理能力,支持将OLAP算子下压到存储层甚至在压缩后的数据上直接做OLAP计算。
雷锋网