作者: 康凯森
日期: 2022-11-17
分类: OLAP
从 2015 年开始,我在美团先后维护和研发过 Apache HBase, Apache Kylin, Apache Druid 和 Apache Doris, 对大数据系统和 OLAP 数据库有了深入理解,2020 年开始,我加入了 StarRocks,我们先后打造了业界领先的向量化执行器,CBO 优化器,Pipeline 并行引擎,支持高效 Update 的存储引擎和极速的数据湖分析,支持存储分离的 Cloud Native 版本也即将面世,一路走来,随着我们攻克一个又一个技术难题,解决一个又一个用户需求和难题,服务着越来越多的客户,我对如何打造一个强大且成熟的数据库有了更深的理解,在前一篇文章 如何快速打造一个高性能数据库原型 中,我们看到利用开源系统打造一个数据库原型是如此的简单,在这篇文章中,我会和大家介绍打造一个强大且成熟的数据库都有哪些难点,之后会再用几篇文章介绍我们如何解决这些难点。
作为一家创业公司或者一家大公司的数据库部门,打造一款强大且成熟的数据库都会有下面两个本质矛盾:
各种各样的用户需求,无止境的性能优化,永远也修不完的 Bug,一个又一个 Killer Feature 的打造,时刻不停的代码重构,增加不完的测试 Case,不间断的技术调研和系统设计,这些工作都需要大量人力,但无论哪家公司,人力总是不足的,优秀的数据库人才更是稀缺。
如果数据库的代码不大变,面对的应用场景不大变,我们让一个数据库变稳定会容易很多,但是如果一个数据库的代码在飞速变化和增长,应对的应用场景不断丰富和变化,让一个数据库稳定下来就会很难。
一款数据库想要在产品和商业上取得成功,想在很多竞品中脱颖而出,就必须有清晰的定位和自己的独特性,一款数据库的独特性就是自己的卖点,比如目前 StarRocks 的卖点就是极速统一。 一旦定位和独特点定义清楚,也便意味着全公司产研资源的重心和方向的倾斜。 而一款数据库想要让自己的独特性成为 Killer Feature,就必须超越所有竞品,这就意味着,这家数据库公司必须有足够强大的创新能力,足够强大的工程能力,才能做到其他数据库公司一定时间内无法做到的。
一个数据库的架构决定了一个数据库未来的上限,决定了一个数据库未来支持更多用户需求的难易程度,如果架构设计有误,后面的很多功能和优化成本就会很高,甚至无法实现。不过在当下,云时代数据库的架构逐渐趋同,想要有足够的独特性和明显的优势还是比较困难。
一般数据库在产品功能上肯定会推出几个 Killer Feature, 作为自己产品的卖点,但是想做作为 Killer Feature,就必须比竞品好很多,这时候就必然需要 Killer Feature 拥有一定的创新性和独特性。
用户越来越多,用户的需求自然就会越多,有些用户的需求很小众和奇葩,但有时候为了拿下一个用户,我们却不得不做,而且有时候做了还会给系统带来额外的复杂性。
比如 CMMI 认证,通信认证,信创认证,国产服务器认证等等,这些认证里面的一些功能其实并不是你的数据库产品所必须的,做了不会增加什么产品竞争力,但是我们为了获取客户,我们还是必须投入大量的人力去通过这些认证。
当一个新系统越来越好,要扩大市场规模,必然要去替换用户已有的旧数据库,但是替换过程中经常会遇到语法,函数,功能不对齐的场景,这时候,如果这个用户很重要,你就不得不去做一些和传统数据库功能对齐的事情。
单核性能优化是一个没有止境的过程,我在 StarRocks 技术内幕:向量化编程精髓 一文中已经解释了单核优化的关键点和方法论,所有算子和函数的深度优化是需要数十人年的事情
单核性能走到世界第一后,我们投入了大量精力在优化多核性能,多核扩展性的瓶颈定位相比单核会更困难些,我们已经做了大量的优化,但还远远不够。不同类型的查询在高并发下会遇到不同的瓶颈:Lock,线程池,RPC,调度问题,NUMA,CPU Cache,内存管理,IO 异步等。
多机的扩展性的常见瓶颈点主要包括:
存储引擎需要在导入性能和查询性能之间进行权衡,一般情况下,导入时候做的事情越多,查询时候的代价就更低些:
我们数据库一般都是面向通用场景开发,但是在特定场景下,我们可以获取更多信息和上下文,这时候,我们就可以进行更多的针对性优化,但这样带来的问题就是,系统的代码逻辑更加复杂,测试和维护的难度更高。
当系统复杂之后,多人协作经常会出现的问题是,一个人之前精心写的一段对性能影响很大的代码,被后人不小心改掉了;或者是优化 A 优化了某些场景的性能,但是却导致之前优化 B 的优化失效了。
数据库这个复杂的软件是构建在硬件之上的,硬件是决定性能的基础,但是 CPU 有不同的型号,网络带宽有高有低,磁盘的吞吐和 IOPS 也有很大的差别,有可能一些软件层面的优化只对某些硬件环境生效。
一款数据库的成熟,主要体现在稳定性上,而打造一个稳定的数据库有哪些难点呢:
每个用户的硬件配置,环境信息,应用特点不一样,都可能引发不同的问题。
很多时候,一些稳定性,扩展性问题只有当集群规模达到一定程度,当数据量大到一定程度,当并发高到一定程度,才会暴露出来,而如何在产品发布之前在有限的资源下,通过测试暴露这些问题,也是一个难点。
很多时候,一个功能单独 Work 没有问题,但是多个功能相互影响时,一些 Bug 才会暴露出来,比如节点下线,触发数据的均衡和复制,又会触发数据版本的问题,进而触发查询的问题;比如导入,查询,Compaction,系统任务对 IO 资源的共同影响,这些复杂功能组合时的测试难度会更大。
比如 Date 类型和数字的比较,字符串和数字的比较,应该是直接报错,还是隐式转换,隐式转换的公共类型转成啥。 这些其实都没有统一的标准,每家数据库都有自己的实现。关键问题是即使我们改成最合理的表现,一些用户可能会因为习惯自己熟悉的数据库的表现,觉得最合理的表现是不合理的。
还有聚合函数溢出后行为,Decimal 运算精度的确定,函数一些异常行为是抛异常还是转 Null, 我们不仅要兼容用户期望的行为和正确性,还要兼顾正确性和性能。
由于快速迭代和发展,我们必然要维护很多版本,这给稳定性提出了更大的挑战:
当我们维护很多个版本后,有时候不得不做一些不兼容的改动:
查询层是无状态的,所以不兼容的改动一般可以绕过或者改 SQL 解决,但是存储层和元数据层因为是有状态的,一旦有不兼容的问题会很麻烦。
对于类似 StarRocks 这种多进程,多语言的数据库,在稳定性问题上还有一个额外的挑战是:
这几年来,我遇到过挺多这样的 Case。
一般编译器被各种项目大量使用,我们开发者遇到 Bug 的机会比较少,不过在开发 StarRocks 向量化的过程中,我就遇到了一个编译器的 Bug: 编译器进行向量化优化后,把 Boolean 值转成了 255,https://gcc.gnu.org/bugzilla/show_bug.cgi?id=97255。 还有最近我们同学遇到的一个 C++ 正则标准库的 Bug: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86164#c8
现在的数据库几乎都是分布式数据库,所以分布式系统遇到的问题,一般数据库都会遇到,比如分布式系统的常见挑战:单点故障,部分失败,不可靠的网络,不可靠的时钟。
数据库的存储引擎必须保证数据能被正确的存储和读取,但是我们在实际环境中不可避免的会遇到各种硬件故障:磁盘坏掉,磁盘数据错误,服务器宕机,机房断电,网络异常等。
市面上流行的 BI 工具多达几十种,每种 BI 工具深入使用后都或多或少会有些兼容性问题:Session 变量的兼容性;数据类型的兼容性;函数的兼容性等,要都完美兼容,必然有大量的人力投入。
要支持用户从传统数据库和各种竞品数据库顺滑迁移,我们就必须和各种数据库进行对接,这里面有大量琐碎的工作。
比如我们想将 Presto 的用户迁移到 StarRocks 上,我们就需要在 SQL 语法层进行兼容。
比如单机数据库上的一些很容易实现的功能,在分布式数据库上就会相对比较困难,这时候如果为了功能对齐,就需要不少的工作。
安全包括:认证,鉴权,审计,加密,脱敏等,这里面每一项都有着大量的工作,对于金融,政府客户,安全体系要求很高;在公有云上,安全要求会更高。
在未来,毋容置疑,易用性会越来越重要,将会是一款数据库的核心竞争力,数据库分析师需要知道的数据库知识会越来越少,需要进行的操作会越来越少。 易用性主要体现在 3 个层次:
当你将这些点一个一个深入思考下去,你肯定会理解打造一个强大且成熟的数据库是多么困难,那么这些难题如何解呢?我之后会用几篇文章讲讲我的一些思考,欢迎关注我的微信公众号,第一时间获取文章推送。