包含rust语言go通讯模型的词条( 三 )


基础网络库的缺失,虽然那个时候 Rust 已经出了 1.0 , 但我们发现很多基础库都没有,譬如在网络库上面只有 mio,没有好用的 RPC 框架,HTTP 也不成熟 。
但我们还是决定使用 Rust,对于第一点,我们团队花了将近一个月的时间来学习 Rust,跟 Rust 编译器作斗争 , 而对于第二点,我们就完全开始自己写 。
幸运的,当我们越过 Rust 那段阵痛期之后 , 发现用 Rust 开发 TiKV 异常的高效,这也就是为啥我们能在短时间开发出 TiKV 并在生产环境中上线的原因 。
一致性协议
对于分布式系统来说 , CAP 是一个不得不考虑的问题,因为 P 也就是 Partition Tolerance 是一定存在的,所以我们就要考虑到底是选择 C - Consistency 还是 A - Availability 。
我们在设计 TiKV 的时候就决定 - 完全保证数据安全性,所以自然就会选择 C,但其实我们并没有完全放弃 A,因为多数时候,毕竟断网 , 机器停电不会特别频繁,我们只需要保证 HA - High Availability,也就是 4 个 9 或者 5 个 9 的可用性就可以了 。
既然选择了 C,我们下一个就考虑的是选用哪一种分布式一致性算法,现在流行的无非就是 Paxos 或者 Raft,而 Raft 因为简单 , 容易理解,以及有很多现成的开源库可以参考 , 自然就成了我们的首要选择 。
在 Raft 的实现上,我们直接参考的 etcd 的 Raft 。etcd 已经被大量的公司在生产环境中使用,所以它的 Raft 库质量是很有保障的 。虽然 etcd 是用 Go 实现的 , 但它的 Raft library 是类似 C 的实现,所以非常便于我们用 Rust 直接翻译 。在翻译的过程中 , 我们也给 etcd 的 Raft fix 了一些 bug,添加了一些功能,让其变得更加健壮和易用 。
现在 Raft 的代码仍然在 TiKV 工程里面,但我们很快会将独立出去 , 变成独立的 library , 这样大家就能在自己的 Rust 项目中使用 Raft 了 。
使用 Raft 不光能保证数据的一致性,也可以借助 Raft 的 Configuration Change 机制实现系统的水平扩展,这个我们会在后面的文章中详细的说明 。
存储引擎
选择了分布式一致性协议 , 下一个就要考虑数据存储的问题了 。在 TiKV 里面 , 我们会存储 Raft log , 然后也会将 Raft log 里面实际的客户请求应用到状态机里面 。
首先来看状态机 , 因为它会存放用户的实际数据,而这些数据完全可能是随机的 key - value,为了高效的处理随机的数据插入 , 自然我们就考虑使用现在通用的 LSM Tree 模型 。而在这种模型下,RocksDB 可以认为是现阶段最优的一个选择 。
RocksDB 是 Facebook 团队在 LevelDB 的基础上面做的高性能 Key-Value Storage,它提供了很多配置选项,能让大家根据不同的硬件环境去调优 。这里有一个梗,说的是因为 RocksDB 配置太多,以至于连 RocksDB team 的同学都不清楚所有配置的意义 。
关于我们在 TiKV 中如何使用,优化 RocksDB,以及给 RocksDB 添加功能,fix bug 这些,我们会在后面文章中详细说明 。
而对于 Raft Log,因为任意 Log 的 index 是完全单调递增的,譬如 Log 1,那么下一个 Log 一定是 Log 2,所以 Log 的插入可以认为是顺序插入 。这种的,最通常的做法就是自己写一个 Segment File,但现在我们仍然使用的是 RocksDB,因为 RocksDB 对于顺序写入也有非常高的性能 , 也能满足我们的需求 。但我们不排除后面使用自己的引擎 。
因为 RocksDB 提供了 C API , 所以可以直接在 Rust 里面使用,大家也可以在自己的 Rust 项目里面通过 rust-rocksdb 这个库来使用 RocksDB 。

推荐阅读