选择“网红语言”值不值?使用Go和Rust的数据库公司七年经验总结

作者 | 赵钰莹  

“我现在会很辩证地看待这件事情,只能说是不好不坏,但当时所谓的主流选择可能会让我们的产品变成一个平庸的系统。”

即便是在此时此刻创业的公司,公司的产品决定全部采用 Go 和 Rust 也是非常艰难的决定,更何况是七年前

2015 到 2016 年,Go 不到五岁,Rust 还没发布 v1.0 版本,没有太多公司和开发者看好这两种语言,怎么会有公司选择全面采用这两种语言,还是用来写数据库和存储层代码?

如果你在七八年前听到这个故事,直觉大概是这家公司活不了太久。事实上,这家公司不仅走过了七年,还拿到了三轮融资,这家公司就是 PingCAP。

当然,编程语言只是工具,绝不是 PingCAP 可以走到今天最重要的因素,但这家公司确实因为这一选择收获了不少。本文通过与 PingCAP 创始人之一黄东旭和两位工程师的交谈,还原了这家创业公司最初选择 Go 和 Rust 的原因,以及如何解决随之而来语言本身的问题、人才问题以及对不同语言适用场景的思考。

1 选择背后的原因  

“首先,我不是某一个具体的编程语言或者工具的信仰者,但在做项目时选择一个好的工具也是十分必要的。”

——黄东旭

选择的第一个要点:开发效率  

Go 语言的缔造者中有一位全世界程序员公认的大神级人物——肯尼思·汤普森(Kenneth Thompson),他是 UNIX 操作系统的主要开发者;其另一位主要设计者和早期实现者罗布·派克虽然没有直接参与最初版本的 UNIX 的开发,但同样属于贝尔实验室 UNIX 开发组的最资深成员,并且是字符编码 UTF-8 的主要实现者。

所以,Go 的出身决定了该语言具备极高的品质。而且,Go 语言从第一个版本起就开源,所以来自世界各地的程序员第一时间发现并使用了它,然后立刻就被其美妙的语言特性所吸引,比如 Go 语言使用比线程轻量得多的 goroutine 完成上下文切换可以节省高达 80% 左右的时间,这些关注者中就包括黄东旭与另一位合伙人刘奇。

早在创办 PingCAP 之前,黄东旭与刘奇就曾使用 Go 语言写过一个叫 codis 的开源软件解决当时豌豆荚业务在缓存扩展方面的问题。深入了解 Go 语言之后,二人被 Go 带来的效率提升所吸引。“同样的系统使用 C++ 开发可能需要一个月,但使用 Go 可能仅需要三天,这种级别的开发效率提升很难不让人动心。”

过去,因为效率而选择一门新兴语言的人并不是没有,比如《黑客与画家》的作者保罗·格雷厄姆 Paul Graham 曾用 Lisp 写了最早的 Web 应用 Viaweb,最终被雅虎以 5 千万余美金收购。这个故事也对 PingCAP 创始团队带来了一些影响。

PingCAP 的目标是做一个分布式数据库,也就是现在我们熟知的 TiDB。团队最开始确实考虑过 C++,毕竟大部分成员都有着 C++ 背景,大多还不错的数据库都是用 C++ 开发的,但是 C++ 非常依赖团队内部研发人员的经验、水平以及团队内部相关规范的制定,否则很容易出现问题。对于刚刚创业的 PingCAP 而言,团队显然是很难找到厉害的 C++ 研发人才。

“这里还有一个有趣的现象,业内鲜少有程序员承认自己精通 C++,即便是拥有 20 年经验的开发者,但可能学一个月就有人说自己精通 Go 了,复杂性一目了然。”

当时还存在一个安全的选择,那就是 Java。那段时间,主流的分布式系统大部分使用 Java 编写的,比如 Hadoop、Zookeeper、Cassandra、HBase 等。但面向云计算时代,团队认为需要更敏捷、更高效、同时更安全的新方式构建系统。

在当时,同样有很多软件产品选择工具时信奉其可以最大程度榨干硬件性能。“老实讲,我并不信奉开发出来的产品一定要能榨取硬件的最后一点性能,招聘具备这种能力程序员的成本可能并不比加几台机器低(分布式时代,这个问题显然可以通过加机器的方式来解决),而且用户也不会因为榨取的这一点性能而付更多钱”,黄东旭在采访中如是说道。

综合权衡下来,Go 成为了当时创业最合适的起步选择,可以快速把 TiDB 的原型开发出来。当年的 9 月份,TiDB 就在 GitHub 上开源了,后续的迭代也很快,随后一年就有客户在生产环境试用,再往后一年 TiDB 1.0 GA 版本正式发布。

选择的第二个要点:语言本身的特性  

既然 Go 的效率得到了验证,团队为什么在后来开发 TiDB 的存储引擎 TiKV 时又选择了 Rust 呢?

TiKV 起始于 2015 年底,当时团队在 Pure Go / Go + Cgo / C++11 / Rust 几个语言之间纠结,虽然 PingCAP 的核心团队有大量的 Go 语言开发经验,另外 TiDB 的 SQL 层已经完全采用 Go 语言开发,Go 带来的开发效率的极大提升也让团队受益良多。但是在存储层的选型上,团队首先排除的就是 Pure Go 的选项,理由很简单,底层已经决定接入 RocksDB,RocksDB 本身就是个 C++ 的项目,而 Go 的 LSM-Tree 的实现大多成熟度不太够没有能和 RocksDB 相提并论的项目,如果选 Go 的话,只能选择用 Cgo 来 bridge,但是当时 Cgo 的问题同样明显,在 2015 年底,在 Go code 里调用 Cgo 的性能损失比较大,并不是在 goroutine 所在的线程直接 Call cgo 的代码,而且对于数据库来说,调用底层的存储引擎库是很频繁的,如果每次调用 RocksDB 的函数都需要这些额外开销,非常不划算,当然也可以通过一些技巧增大 Cgo 这边的调用的吞吐,比如一段时间内的调用打包成一个 cgo batch call,通过增加单个请求的延迟来增大的整体的吞吐,抹平 cgo 调用本身的开销,但是这样一来,实现就会变得非常复杂。另一方面,GC 问题仍然没有办法彻底解决, 存储层希望尽可能高效的利用内存,大量使用 syscall.Mmap 或者对象复用这些有些 hacky 的技巧,会让整体的代码可读性降低。

其实 C++11 也没什么问题,性能上肯定没问题,RocksDB 是 C++11 写的,在纠结了一小段时间后,团队认真评估了成员背景和要做的东西,最后还是没有选择 C++,原因主要是:

核心团队过去都是 C++ 的重度开发者,基本都有维护过大型 C++ 项目的经历,每个人都有点心里阴影… 悬挂指针、内存泄漏、Data race 在项目越来越大的过程中几乎很难避免,当然你可以说靠老司机带路,严格 Code Review 和编码规范可以将问题发生的概率降低,但是一旦出现问题,Debug 的成本很高,心智负担很重,而且第三方库不满足规范怎么办。

C++ 的编程范式太多,而且差异很大,又有很多奇技淫巧,统一风格同样也需要额外的学习成本,特别是团队的成员在不断的增加,不一定所有人都是 C++ 老司机,特别是大家这么多年了都已经习惯了 GC 的帮助,已经很难回到手动管理内存的时代。

缺乏包管理,集成构建等现代化的周边工具,虽然这点看上去没那么重要,但是对于一个大型项目这些自动化工具是极其重要的,直接关系到大家的开发效率和项目的迭代的速度。而且 C++ 的第三方库参差不齐,很多轮子得自己造。

Rust 在 2015 年底已经发布了 1.0,Rust 有几点特性非常吸引团队:

  • 内存安全性

  • 高性能 (得益于 llvm 的优秀能力,运行时实际上和 C++ 几乎没区别),与 C/C++ 的包的亲缘性

  • 强大的包管理和构建工具 Cargo

  • 更现代的语法

  • 和 C++ 几乎一致的调试调优体验,之前熟悉的工具比如 perf 之类的都可以直接复用

  • FFI,可以无损失的链接和调用 RocksDB 的 C API

一方面,团队把安全性放在第一位,C++ 的内存管理和避免 Data race 的问题虽然靠老司机可以解决,但是仍然没有在编译器层面上强约束,把问题扼杀在摇篮之中解决的彻底,Rust 很好地解决了这个问题。另一方面,Rust 是一个非常现代化的编程语言,现代的类型系统,模式匹配,功能强大的宏,trait 等熟悉以后会极大提升开发效率。

最终,Rust 也没让团队失望,四五个人的团队从零开始花费了四个月左右的时间就开发出了 TiKV 的第一个版本。2016 年 1 月 1 日开始开发,4 月 1 日开源。同年 10 月份,TiKV 第一次被使用在生产环境,那时 TiDB 甚至都还没有发布 beta 版。TiKV 的开发非常快,发布的版本都很稳定,生产效率比 C++ 高出许多。

选择的第三个要点:人才  

虽然当年并没有太多开发者使用 Rust,但早期探索者们的自身能力是很强的,这群人对编程本身有着极强的热爱,这对于创业公司而言是非常宝贵的人才(起步阶段的创业公司人才在精不在多),PingCAP 后续的发展也验证了这一点,曾经是 Rust 核心团队成员的 Brian Anderson 在 2018 年选择加入该公司并参与 TiKV 的研发,这种案例在 2018 年之前是极少数的。Brian 之所以愿意加入,除个人因素之外,与 PingCAP 在开源和社区方面的持续努力是分不开的。2017 年, Brian 就应邀出席过 PingCAP 举办的国内首场 Rust Meetup。2019 年, Rust Core Team 的元老 nrc 也加入了 PingCAP。

“直到今天,我依旧认为这是创业公司吸纳人才时很好的思路,否则一家创业公司通过什么去跟互联网大厂竞争,只能是更好的理念和工具。我们在没有做任何全球化品牌的时候就是靠着这张名片(指全面拥抱 Rust 生态)吸引人才加入我们的社区和公司。但很多时候,国内很多公司的问题是不敢想,认为自己凭什么可以吸引到这样的大牛加入,但凡事都要先试试。”

2 选择 Rust 带来的问题  

如开篇所言,黄东旭如今认为当初的决定“不好不坏”,虽然获得了开发效率上的提升,也承受了当时语言不够成熟带来的问题。

“很多时候,人们会先通过广告了解一件事情。我们当初对 Rust 的看法也是内存安全、性能好,没有 GC 效率肯定高等,实际并不是这样的。如果你代码写的很挫,凭什么认为自己手动分配内存就比 GC 搞得好。”

起初,团队基本是把 Rust 当成 Java、C++ 在用,性能并没有明显提升,直到更专业的人才加入才把整个代码扭转到更好的道路上。

此外,Rust 的编译时间较长。“当时我们内部的 Rust 程序员经常开玩笑说一天只有 24 次编译机会,用一次少一次”,团队做了大量工作去降低 Rust 的编译时间,参与并贡献了 rust-gRPC、Raft 库,open-tracing 等项目中,并产出了大量相关的博客文章。

虽然解决这些问题占用了团队的很多时间,但团队也因此获益。“我们将 Talent Plan 的所有教程用 Rust 实现了一遍,虽然这离我们的主页主业有点远,但对后来的招聘和培训极其重要,这也是我们第一次在全球范围内好评如潮。”

3 怎么判断要不要选择或者切换 Rust、GO?  

不少企业创业之初会选择基于某些开源产品来实现商业版本,这种情况下编程语言其实已经是确定的了,不需要太过纠结。如果从零开始开发某项产品,可以从以下几个方面进行考虑:

  1. 如果公司内部在 C/C++、Java 上开发规范已经做得很好了,可以先不考虑切换至 Rust。Rust 相当于自带严格的安全性限制,让程序员在大部分情况下没办法写出存在安全隐患的代码,语言本身的设计帮助规避了一些常规问题。

  2. 如果是基础软件类型的企业,相关从业人员的水平还是值得信任的,一般不会犯太多低级错误。此时,Rust 的收益主要体现在数据库最核心的组件或者功能编写上,对剩下 90% 的部分而言,效率可能比安全更重要。

  3. 非 Rust 不可的场景有写驱动,比如操作系统内核等,效率绝对高;SSL 加密或者产品内部的某个关键链路,比如浏览器里面的渲染引擎,这类 CPU 密集型又对安全要求较高的场景。非必要场景最好选用社区比较大的编程语言,比如 Java,相对来说也很好招人。

  4. 考虑语言的向后兼容性。Go 语言在这一点上至今都做得非常好,并且也响应社区用户的意见添加了范型。

  5. 社区的风格。Rust 的社区是非常开放的,这给该语言带来了很多好处,但也可能带来一些副作用,比如可能会有些分裂,这可能是 Rust 社区未来要考虑的事情,但社区内的氛围相对活跃,可以在其中寻找问题的答案或者志同道合的朋友。

    “Rust 近几年最新加入的复杂度主要是 Pin 异步编程,但二者的加入确实解决了一些问题,无论是哪种语言都会面临各种选择,这是不同目标平衡取舍的结果。”

  6. 代码实现逻辑。Rust 语言在设计上与 Go、Java 等都不同,其规避了一些问题。使用 Rust 写出来的程序可以专注优化程序逻辑本身,比如让程序更加适应操作系统、减少线程切换等。

Rust 如今已经得到了越来越多企业和开发者的验证,但依旧有新的小众语言出现,如同当年新生的 Rust,企业应该如何判断呢?

4
新型语言 Zig 也来了,怎么看?  

Zig 就是一门新的、小众的语言,目前还没有发布 1.0 版本,其将竞争对手定为了 C 语言,注意不是 C++,而就是最基础的 C,但也吸收了 Rust 等语言的一些特性。

去年中旬因为 Uber 的使用,Zig 引起了一些开发者的关注。Uber 使用 Zig 来编译其 C/C++ 代码。现在,Uber 只在 Go Monorepo(据其内部工程师介绍,Go Monorepo 比 Linux 内核还要大,有几千名工程师在开发和维护) 中使用 bazel-zig-cc,但计划尽可能地将 zig cc 推广到其他需要 C/C++ 工具链的语言。

Uber 的工程师表示,与其他工具链相比,zig-cc 提供的 C/C++ 工具链的主要优势是 glibc 版本可配制与 macOS 交叉编译(具体信息可以阅读:https://www.infoq.cn/article/q016NWR7OjHvOJ3Rkzc0)。

如今的 Zig 与七八年前的 Rust 情况相似又不同,当年的 Rust 背后有一群 Molliza 的工程师支持,其理念、质量和解决实际问题的能力是值得信任的,现在的 Zig 虽然有基金会支持,但实际能力还有待商榷。

受访的 PingCAP 工程师们对该语言的发展持观望态度,并建议企业在选择这类新兴小众的语言时最好是可以找到那个“非它不可”的理由,如果找不到,就证明这件事情没必要冒险采用新语言,如果有想法,最好可以亲自与创始团队面对面交流,从中得到一些判断。

5 结语  

自从 ChatGPT 诞生,我们无数次对 AI 的生产力感到惊讶,其可以根据简单的输入生成代码并将这些代码轻松转换成其他编程语言。未来,开发者的工作模式可能会发生翻天覆地的变化,最重要的是我们构建这一切的思路和想法,而不是工具本身。从这个角度出来,关于编程语言优劣的争论就会变得不那么重要。

当然,如果你是某一个语言 / 工具的信仰者,着手将其带向全新高度也会比争论更有意义。

本文文字及图片出自 InfoQ

余下全文(1/3)
分享这篇文章:

请关注我们:

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注