写点什么

我们为什么要选择 Rust 而不是 Golang 或 C/C++ 来开发 TiKV ?

发布于: 18 小时前

Translated by Turing Zhu

Original Article: whyrust

什么是 Rust ?

Rust 是一门由 Mozilla Research 赞助的系统编程语言。Rust 发展迅速并从 2015 年 5 月份发布 1.0 版本以来,固定每 6 周 1 个发行版 Rust 本。


下面列表中是最吸引我们的一些特性:


  • 在抽象上,Rust 的设计理念类似于 C++ ,但没有额外的开销以及 RAII(资源获取需要初始化)。

  • 最小的运行时和高效的 C 绑定让 Rust 能够像 C/C++ 那样高效,从而使 Rust 非常适合于高性能最重要的系统编程领域。

  • 强大的类型系统以及独特的生命周期管理便于编译期间的内存管理,保证了内存安全以及线程安全的同时,让编译后的程序运行快速。

  • Rust 像函数式编程语言那样提供模式匹配和类型引用,与此同时代码也更简洁优美。

  • 在工程化尤其涉及到库时,宏和 trait 让 Rust 更加抽象并保留相当少的样板。

Rust 生态

得益于卓越的保管理工具 - Cargo,Rust 有许多库类型,如 HTTP 的 Hyper 以及异步 I/O 的 Tokio 和 mio 等基本上所有用于构建后端应用所需的库。


一般来说,Rust 现阶段主要用于开发高性能的服务端应用。此外,在类型系统和语法方面的创新使其在开发领域特定库(DSL)方面独具优势。

使用 Rust

作为一门新兴编程语言来说,Rust 是独一无二的。这里列出一些使用 Rust 的项目,


  • Dropbox,分布式存储系统后端

  • Servo,Firefox 的新内核

  • Redox,新操作系统

  • TiKVTiDB的存储布局,一种由PingCAP开发的分布式数据库。


根据 Github 的趋势,作为 Rust 的之友之一的 TiKV 一直是 Rust 的顶级项目之一。


TiKV 是一种分布式 K-V 数据库。他是 TiDB 项目的核心组件,同时是 Google Spanner 的一个开源实现。我们选择 Rust 从头开始构建如此大的分布式存储项目。我会在该博客中揭示其基本原理。


在相当长的一段时间内,C 或 C++ 主导了 像数据这样的基础设施软件的开发。Java 或 Golang 有 GC 抖动这样的问题,尤其是在高读/写压力下。一方面,协程-轻量级线程以及 Golang 的迷人的特性,以 Golang 运行时上下文切换的额外开销为代价,显著地降低了开发并发应用程序的的复杂性。对于像数据库这样的基础设施软件来说,性能的重要性无可厚非。另一方面,系统需要保持其“确定性”,以便于进行性能调优。但是引入 GC 和另一个运行时会导致相反的结果。所以一直以来 C/C++ 似乎是唯一的选择。


TiKV 于 2015 年底问世。我们的团队在不同的语言选择之间纠结:纯 Go、Go + Cgo、C++11 或者 Rust。


  • 纯 Go:我们的核心团队在 Go 上有丰富的经验。TiDB 的 SQL 层由 Go 开发,并且 Go 带来的高效率让我们受益匪浅。但是,当涉及到存储层的开放时,纯 Go 是第一个被排除的选项,原因很简单:我们决定用 RocksDB 作为底层,因为它是用 C++ 开发的。Go 中现有的 LSM 树实现(与 goleveldb 类似)几乎没有 RocksDB 成熟。

  • Cgo:如果使用了 Go,那么必须使用 Cgo 来做桥接,但 Cgo 有其自身的问题。在 2015 年底,如果在 Go 代码中调用 Cgo 而不是与协程在同一个线程中调用 Cgo,那么性能可能会受到很大影响。而且,数据库需要频繁地调用下面的存储库,又名 RocksDB。如果每次调用 RocksDB 的功能都需要额外的开销,那么效率就会非常低。当然,可以引入一些变通方法来扩大 Cgo 调用的吞吐量,如将调用打包到一个确定的时期给 Cgo 批量调用,这会增加单个请求的延迟但消除了 Cgo 的开销。但是,在 GC 问题没有全部解决之前这个实现可能非常复杂。在存储层,我们希望尽可能高效地利用内存。诸如大量使用syscall.Mmap 或对象重用之类的变通方法可能会破坏代码的可读性。

  • C++11:使用 C++11 当然绝对没有问题。RocksDB 就是用 C++11 开发的。但考虑到团队背景以及我们想要做的,我们并没有选择 C++11。原因如下:

  • i. 核心团队成员都是有经验的 C++ 开发人员并且都有大型 C++ 项目开发经验。但是在大型项目中似乎都有不可避免的问题,像大零指针、内存泄漏或数据竞争这样的问题让他们一想起就不寒而栗。当然,如果引导得当,这样的问题出现的可能性会降低或者制定严格的代码审查与编码规范。但如果出现问题,那么调试起来可能代价高昂且负担沉重。更不用说第三方库不能满足我们的编码规范,我们也无法控制。

  • ii. 在 C++ 中有太多太多的不同编程范式以及太多的技巧。统一编码风格需要额外的成本,尤其是当有越来越多的新成员可能不熟悉 C++ 的时候。在使用了带 GC 的语言数年之后,很难回到手动管理内存的时代。

  • iii. 缺乏包管理与 CI 工具。这看似微不足道,但对于一个大型项目来说,自动化工具相当重要,因为它直接关系到开发效率与迭代速度。此外,C++ 库远远不够甚至其中一些需要我们自己创建。

  • Rust:Rust 的 1.0 版本发布于 2015 年 5 月份,有一下迷人的特性:

  • i. 内存安全** **

  • ii. 由 LLVM 保证的高性能。运行时与 C++ 几乎无差别。同时还与 C/C++ 包具有亲和力。

  • iii. Cargo,强大的包管理工具

  • iv. 现代语法

  • v. 几乎一致的故障排查和性能调优体验。我们可以直接重用一些工具,像我们已经非常熟悉的 perf。

  • vi. FFI(外部函数接口),直接无损地调用 RocksDB 中的 C API。


首先第一原因是内存安全。就像上面提到的那样,在内存管理和数据竞争方面的问题对于 C++ 老手来说似乎相当容易。但我相信 Rust 正在做的最大的解决方案是在编译器中设置约束并从一开始就解决掉。对于大型项目来说,永远不要把质量只压在人身上。人非圣贤,如能无过。尽管 Rust 刚开始比较难,但我认为这相当值得。而且,Rust 是一门非常现代化的编程语言,具有特殊的类型系统,模式建模,强大的宏,特征等。一旦你熟悉了 Rust, 其可以大大提高效率,这可能与我们选择 C++ 来调试的计算时间一样。根据我们的经验,对于软件工程师来说,从零基础到编码大约花费 1 个月的时间。经验丰富的 Rust 工程师和 Golang 工程师的效率几乎一样。


总之,Rust 作为一门新兴编程语言来说,对于中国的大多数开发者而言似乎是新颖的,但其已经成为 C/C++ 最具前途的挑战者。

用户头像

还未添加个人签名 2018.11.07 加入

还未添加个人简介

评论

发布
暂无评论
我们为什么要选择 Rust 而不是 Golang 或 C/C++ 来开发 TiKV ?