第11章 质量保证、运维及实践
OceanBase系统一直在不断演化,需要在代码不断变化的过程中保持系统的稳定性。因此,合理的质量保证体系关乎系统的成败。为了保证系统质量,OceanBase做了大量工作,在RD(指开发工程师)开发、QA(指测试工程师)测试、上线试运行各个阶段对系统质量把关。
系统的性能和稳定性得到保障后,还需要具备良好的可运维性。OceanBase借鉴了Oracle数据库中的“系统表”机制,将表格Schema、监控数据、系统内部状态等信息保存到内部系统表中,从而能够基于系统表构建监控界面、运维管理界面以及运维工具。
最后,系统只有通过上线使用才能证明自己并发现设计和实现上的不足。本章首先介绍OceanBase的质量保证体系和运维体系,接着以收藏夹、天猫评价和直通车报表为例介绍OceanBase系统的使用情况。最后,笔者总结了实践过程中的经验教训。
11.1 质量保证
互联网基础产品的质量保证不只是QA的事情,从RD设计、编码开始,系统提测,直至最后上线,每个环节都需要重视质量保证工作。OceanBase的质量保证体系如图11-1所示。
图 11-1 OceanBase质量保证体系
一个新版本需要经过开发=>单元测试&快速测试=>RD(开发工程师)压力测试=>系统提测=>QA(测试工程师)接口、功能、容灾、压力测试=>兼容性测试=>Benchmark测试才能最终发布,其中,RD压力测试和兼容性测试是可选的。发布的新版本还需要经过业务压力测试或者线上流量回放才能上线试运行,试运行一段时间后没有发现问题才能最终上线。
11.1.1 RD开发
系统Bug暴露越早修复代价越低,开发工程师是产生Bug的源头,开发阶段主要通过编码规范、代码审核(Code Review)、单元测试保证代码质量。另外,系统提测前RD需要主动执行快速测试(quicktest),从而避免返工。
1.编码规范
编码规范规定了函数、变量、类型的命名规则,保证统一的注释和排版风格。除此之外,为了避免C/C++服务器端编程常见缺陷,OceanBase编码规范还制定了一些规则,如下所示:
1)一个函数只能有一个入口和一个出口。不允许在函数中使用goto语句,也不允许函数中途return返回。
如图11-2所示,左边的代码中途调用了return,在OceanBase编码规范中是不允许的,可以修改为右边的方式。这条规定有一定的争议,很多优秀的开源项目都允许函数中途return。之所以这么规定,是为了确保函数执行过程中申请的资源被释放掉。对于分布式存储系统,代码稳定运行的重要性远远高于代码写得更漂亮。
图 11-2 单入口单出口
2)禁止在函数中抛异常,谨慎使用STL、boost。C/C++编程的麻烦之处在于资源管理,尤其是内存管理。STL、boost库接口容易使用,能够提高编码效率,但是内存管理混乱,不易调试,且大多数开发工程师不了解其内部实现,不适用于高性能服务器的开发。
3)资源管理做到可控。所有的内存申请操作都需要经过OceanBase全局内存管理器,不允许直接在代码中调用new/malloc申请内存。另外,系统初始化时启动所有线程,执行过程中不允许动态启动额外的线程。
4)每个可能失败的函数都必须返回错误码,0表示成功,其他值表示出错。调用者需要仔细、全面地处理调用函数返回的每个错误码。
5)所有的指针使用前都必须判空,不允许使用assert[1]语句替代错误检查。这条规定是为了保证程序执行过程中出现异常情况时能够打印错误日志而不是core dump。
6)不允许使用strcpy/strcat/strcpy/sprintf等字符串操作函数,而改用对应的限制字符串长度函数:strncpy/strncat/strncpy/snprintf,从而防止字符串操作越界。
7)严格要求自己,编译时要开启GCC所有报警开关,例如:-Wall-Werror-Wextra-Wunused-parameter-Wformat-Wconversion-Wdeprecated。代码提交前需要确保解决所有的报警。
2.代码审核
OceanBase开发时要求所有代码提交前至少由一人审核,对于关键代码改动,例如,紧急修复线上Bug,需要架构师和各个小组的技术负责人参与。
代码审核工作主要包含两个部分:编码风格审核,比如是否符合编码规范,接口设计是否合理,以及实现逻辑审核。其中,实现逻辑审核是难点,要求理解每个代码实现细节,并给出建设性意见。每个刚刚加入团队的新人都会分配一个师兄,师兄的其中一项职责就是审核新人的代码,与新人一起共同对代码质量负责。
OceanBase采用开源的ReviewBoard(http://www.reviewboard.org/)作为代码审核系统,如图11-3所示。
图 11-3 OceanBase ReviewBoard
3.单元测试
OceanBase采用google test以及google mock进行单元测试。单元测试的关键点在于系统接口设计时考虑可测性,并提高每个开发人员的单元测试意识。
OceanBase单元测试已接入一淘网内部开发的Toast平台,每天晚上会自动回归所有的单元测试用例。Toast平台说明文档见:http://testing.etao.com/book/export/html/285。
4.快速测试(quicktest)
快速测试选取所有测试用例的一个子集,这个子集中的每个用例执行都很快,从而做到快速回归。快速测试部署成定时任务,每天自动回归,RD提交某个功能的代码之前也会主动运行快速测试,从而使得主干代码保持基本稳定。
5.RD压力测试
(1)分布式存储引擎压力测试
分布式存储引擎压力测试工具包含两个:syschecker以及mixed_test。
在syschecker工具中,多个客户端并发读写一行或者多行数据,并对读取到的每行数据进行校验。对于每行数据,其中的每一列都对应一个辅助列,二者数据之和为0。假设某列数据出错,syschecker能够很快检测出来。
syschecker写入速度很快,能够发现分布式存储引擎中的大部分问题,然而,syschecker只校验单行数据,不校验多行数据之间的关系。因此,syschecker无法发现某行数据全部丢失的情况。mixed_test正是用来解决这个问题的,它不仅对每行数据进行校验,还校验多行数据之间的关系,能够检测出某行数据全部丢失的情况。当然,mixed_test写入速度较慢,syschecker和mixed_test两个工具总是配合使用,各有优势。
(2)数据库功能压力测试
数据库功能压力测试工具包含两个:sqltest以及bigquery。
●sqltest工具测试时将指定一些SQL语句,sqltest工具会将这些语句分别发送给 MySQL以及OceanBase数据库。如果二者的执行结果相同,则认为sqltest测试通过;否则,测试失败。
●bigquery工具是sqltest工具的补充,专门用于测试OLAP并发查询功能。 bigquery中每个查询涉及的数据往往跨多个子表,能够触发OceanBase的并发查询功能。当然,bigquery灵活性不够,只能执行特定的SQL语句,而sqltest能够执行OceanBase支持的所有SQL语句。因此,bigquery和sqltest两个工具也是配合使用,各有优势。
OceanBase早期测试资源严重不足,因此,要求开发在提测前必须运行一遍压力测试。然而,这些压力测试工具的维护非常耗时。2013年开始,RD压力测试工具逐步废弃,其中的测试用例逐步融合到QA压力测试工具中。
[1]C语言中的宏定义,如果传入条件不成立,程序直接core dump退出。