【CSDN 编者按】无论对于哪一个大的项目来说,重写都不是一个简单的决定。对于编写数据库系统来说,使用C++看起来是个不错的决定,毕竟大多数知名的数据库系统,包括MySQL、PostgreSQL、Oracle和IBM Db2,都是用C/C++创建的。但是一个处在发展前期的初创公司,在经过深思熟虑之后,此公司CEO决定将使用C++开发了7个月的数据库系统重写,并且迁移到了Rust,这是为什么呢?

原文链接:https://singularity-data.com/blog/building-a-cloud-database-from-scratch-why-we-moved-from-cpp-to-rust/

本文由CSDN翻译,未经授权,禁止转载。

译者 | 章雨铭       责编 | 屠敏

出品 | CSDN(ID:CSDNnews)

以下为译文:

作为一家早期的数据库初创公司,本来我们的代码库是用C++写的,开发了7个月之后,完全删除了C++代码库,并且用Rust重写了所有的内容。下面我将讲述这个决定的原因,以及为什么这是最好的决定。

RisingWave是一个云原生流媒体数据库,这个系统是为了降低云中构建实时应用程序的复杂性和成本而建造的。

在2021年初开始构建RisingWave时,是用C++写的。创始团队也是由几位经验丰富(拥有10多年相关经验)的C++工程师组成的。因此,使用C++似乎是理所当然的,没有经过深入的考虑。开发的头几个月很顺利,我们全力以赴地构建这个优秀的数据库,梦想着RisingWave能够撼动现代数据堆栈。

随着团队日益壮大,使用C++导致的缺点开始显现,比如不可读的编码风格、内存泄露、分段错误等等。团队开始怀疑,用C++来编写新数据库系统真的合适吗? 

开发耗时7个月后,经过整整一个月的讨论,我们做出了一个艰难的决定:从C++迁移到Rust。 

这个决定意味着什么?意味着,一个由10多名经验丰富的工程师组成的团队,不得不从头开始写整个系统!我们7个月的努力都白费了。对于一家早期的初创公司来说,这无疑是一个疯狂的决定。因为,在这个竞争激烈的科创公司中,时间就是一切。

做出这个决定后,我们花费了大约两个月的时间完全删除了C++代码库,一共276,406行代码,并且用Rust重写。现在已经过去了10个月,这个决定让RisingWave运行得很好,其源代码可供Apache许可证2.0下的每个人访问。有超过60位贡献者一起开发云原生流媒体数据库。RisingWave能够在重写后幸存下来,而且在短短一个月内在GitHub上获得了超过1,600颗星星,我们为此感到兴奋且自豪。

2319fba2d5314e26d2a00e6352c94f52.png

Rust社区快速增长,许多工程师可能会考虑是否用Rust编写他们的项目,就和当初我们一样。所以我们愿意分享一些经验,关于迁移到Rust的原因,以及在过程中遇到的困难。

首先是对在开发过程中C++带来问题的回顾。

选C++不会错,但它并不总是最好的

C/C++无疑是构建数据库系统最流行的编程语言之一。大多数知名的数据库系统,包括MySQL,PostgreSQL,Oracle和IBM Db2,都是用C / C++创建的。所以它仍然是一种可行,重要且相关的语言。选择C++来构建一个全新的数据库系统不会是一个错误的决定,但这并不意味着C++是最佳选择,特别是对于一个旨在从头开始创新大型数据库系统的早期创业公司来说。

那么使用C++带来了哪些好处和麻烦呢?

好处

  • C++能够让开发人员开发出高性能的程序。它提供了对内存和计算的细粒度控制,而没有自动垃圾回收的成本。此外,C++代码可以编译成汇编语言,以便在操作系统上直接执行,而不是依赖于解释器或语言运行时。

  • C++已经被证明是系统编程的可行语言。大量的数据库都是用C/C++构建的。因此,足以让决策者相信,选择C++不会错。

坏处

  • C++为程序员提供了很大的灵活性,但这是有代价的。编写时非常容易出现错误,而且有的错误会带来严重的后果。调试C++程序非常困难,特别是对于并发编程。

  • 管理依赖关系可能很麻烦。尽管有一些工具(例如 CMake)可以自动配置C++项目的编译,但开发人员仍然需要手动配置和安装依赖库。 

大麻烦

  • STL 库缺乏对某些现代编程工具的支持,例如,本机协同例程支持。因此,开发人员必须依赖许多社区项目,并且大多数缺乏长期支持。

  • 很难保证质量。C++支持如此多的功能,不同的开发人员可以用截然不同的风格编写C++。随着团队日益壮大,可读性就无法得到保证。此外,识别C++代码中的错误也并非易事。因此,审查C++代码可能会变得令人生畏。

Rust是更优的选择

既然用C++来构建数据库系统来说并不算太糟糕,那么为啥那么要重写代码库呢?做出这个决定,我们是经过深思熟虑的。

5d0dc95974dcbed48a069bbc82e559e4.png

流数据库通常用于对延迟非常敏感的关键型任务,因此,只有符合以下要求的语言才能用于构建RisingWave:

  • 保证零成本抽象(构建一个抽象的时候这个抽象不会造成额外的负担),这样就不会有性能上限;

  • 不需要运行时垃圾回收,以便控制可能由内存管理引起的延迟峰值。

为了获得顶尖的性能,在这两个基本要求上不容妥协。

基于以上考虑,Rust成为了更优的选项。虽然这两种语言都能为开发人员提供零成本抽象和对内存管理的完全控制。但是,总的来说,Rust是促进高效和大规模协作的更好选择。以下是四大原因:

  • Rust很安全。Rust 通过引入所有权规则来保证编译时的内存安全和线程安全。它甚至超越了RAII(RAII 是C++中使用的常见内存管理机制)。还有两个优点。第一个是不言而喻的:一旦 Rust 编译器验证了程序,就不会在运行时出现分段错误或数据争用,否则将需要数十个小时的调试,尤其是在高度并发且主要是异步的代码库中。第二个是:Rust 编译器只是限制错误的类型,这减少了可能导致此类错误行为的错综复杂的交织代码片段。

  • Rust易于使用。C++为开发人员提供了最大自由度,但有时,这会适得其反。例如,在C++中,template会在编译时扩展,以以检查特定类型是否不可调用任何操作。在 Rust 中,编译器可以在调用站点检查类型的有效性,而不是运行扩展。这种差异使C++template的错误信息更加难懂,通常需要经验丰富的C++高手来破译。另一个例子是C++中隐式转换的广泛滥用。隐式转换可能会帮助开发者减少编码,但是更容易导致出错。

  • Rust易于学习。对于经验丰富的C++程序员来说,Rust 很容易学习。Rust 对于初学者来说可能具有挑战性。但事实证明,公司的实习生并非如此。他们在一两周内就学会了 Rust——即使之前没有 Rust/C++专业知识——因为需要记住的隐式转换和 重载决议更少。检查基本的 Rust 代码对我们的同事来说轻而易举。现在,花在审查初学者的 Rust 代码上的时间远远少于C++代码。

  • 不安全的Rust是可管理的。 由于Rust静态分析器的保守性,有可能会发生这样的情况:只有不安全的Rust才能使不可能成为可能。一个典型的例子是创建一个自我参照的类型。或者我们必须通过不安全的方式获得额外的性能,即直接操作压缩内存表示中的bit。当然,有人会质疑:这是否会使代码库变得脆弱?对于RisingWave,根据经验,答案是否定的。两个主要的用例是LRU缓存和Bitmap,它们在总共17万行的代码中只占不到200行。采用这种方法,首先在安全的Rust中编码,只有在有具体的证据和坚实的论据时才采用不安全的方法,所以这么做不会让人担忧。

不好的一面

虽然Rust满足了我们的大多数要求,但也有其不好的一面:

  • 分散的异步生态系统:在没有对异步运行时做出初步决定的情况下,我们花了几个月的时间摆脱了futures-rs和async-std,并最终切换到tokio-rs。

  • 繁琐的错误处理:需要手动存储和实现对错误的回溯捕获,以获得回溯。

  • 对异步迭代器的支持不充分。由于traits中没有对稳定生成器和async fn的本地支持,所以我们需要使用第三方库来实现支持。然而,与待定的标准实现相比,这些库分配了额外的Boxes,最终会导致性能的下降。另外,使用这些库中的宏会妨碍IDE的正常工作,阻碍程序员开发。

  • 通用关联类型 (GAT) 的实际限制。GAT是许多现有/待定功能的基础,例如traits中的静态/动态async fn。然而,对GAT的完全支持会产生复杂的技术问题,可能需要比预期更长的时间来解决。在此之前,我们必须使用各种技巧来绕过限制,或者忍受次优的解决方案。

尽管如此,但是我们的团队中有这么多才华横溢的工程师,在控制负面影响的同时,仍然能够显著提高生产力和代码质量。

根据具体情况做决定

这篇博客并不是要说服每个数据库开发团队放弃他们的整个C++代码库,并从头开始在 Rust 中重写他们的系统。相反,这篇博客主要是讲述做出这样决定的原因。事实上,尽管 Rust 带来了明显的好处,但如果没有以下关键因素,我们可能不会做出这个艰难的决定:

  • 当时我们正在重构代码库以适应我们的新系统架构,重写(至少一部分)代码库是不可避免的。

  • 我们团队中有一些Rust爱好者,他们不断向其他工程师宣传 Rust,并说服整个团队,用Rust重写是一个实用的选择。

  • 我们的团队不断壮大,这加快了代码库的重写的效率。

总结

Rust 是一种很酷的编程语言,值得每一个人尝试。但是不要仅仅因此就重写你的项目。如果你正在考虑是否要用Rust重写你的项目,那么请问问自己以下问题:

  • 低级编程、性能、内存安全和包管理是否是您项目所关注的问题?

  • 你的团队中有没有Rust专家可以帮助避免潜在的麻烦?

  • 重写这个项目需要多长时间?

  • 你们会因为重写而错过任何关键的截止日期吗?

  • 你们有关于 Rust 的内部培训计划吗?

你可以在仔细考虑这些问题的答案后再做出决定。当然,Rust(或任何其他语言)永远不会决定你项目的命运。但是,做出明智的选择可能会为你节省数百甚至数千人月的时间。

Logo

20年前,《新程序员》创刊时,我们的心愿是全面关注程序员成长,中国将拥有新一代世界级的程序员。20年后的今天,我们有了新的使命:助力中国IT技术人成长,成就一亿技术人!

更多推荐