[译] 唐刘:Rust in TiKV

这是PingCAP的首席架构师唐刘在 Rust 专场 Meetup 英文演讲稿的翻译篇。鉴于译者水平有限,错误之处还请批评指正。文末可以点击阅读原文查看英文原版。

大家好,今天我想和大家谈谈我们是如何在 TiKV 中使用 Rust 的。

开始之前,请允许我先做下自我介绍。我叫唐刘,PingCAP 的首席架构师。在我加入 PingCAP 之前,我在金山和腾讯工作过。我热爱开源,而且开发过一些项目,比如 LedisDB 、go-mysql 等等。

首先,我会解释下为什么我们选择 Rust 开发 TiKV,然后再向你们简要展示下 TiKV的基本架构,以及一些关键技术。最后,我会介绍下我们未来的计划。

什么是 TiKV

好的,现在开始。首先,什么是 TiKV ? TiKV 是一个分布式的 Key-Value 数据库,有以下几个特点:

  • 异地复制 : 我们使用 Raft 和 Placement Driver 进行异地复制来保证数据的安全性。
  • 水平扩展 : 如果发现快速的数据增长很快就要突破系统容量,我们可以通过直接添加节点的方式来扩展系统容量。
  • 一致性分布式事务:我们使用基于 Google Percolator、优化后的两阶段提交协议来支持分布式事务。你可以使用 “begin” 来开启一个事务,然后做些事情,之后再使用 “commit” 或者 “rollback” 来完成这个事务。
  • 分布式计算的协处理器 : 就像 HBase 那样,我们支持协处理器框架来让用户直接在 TiKV 中做计算。
  • 和 TiDB 融合就像 Spanner 之于 F1 :使用 TiKV 作为 TiDB 的后端存储引擎,我们可以提供最好的分布式关系型数据库。

我们需要一门语言具有...

正如你所见,TiKV 有很多强大的功能。为了开发这些功能,我们也需要一个强大的编程语言。这种编程语言应该具备这些特点:

  • 快速 : 我们非常重视 TiKV 的性能,所以我们需要一个在运行时运行很快的语言。
  • 内存安全:对于一个准备运行很长时间的程序,我们不想遇到任何内存问题,比如野指针、内存泄漏等。
  • 线程安全:我们必须保证数据一直都是一致的,所以任何数据竞争问题都必须避免。
  • 和 C 高效绑定: 我们严重依赖 RocksDB,所以我们必须尽可能快地调用 RocksDB 的 API,不能有一点性能上的下降。

为什么不是 C++?

为了开发高性能服务,在大多数场景下 C++ 可能是最好的选择,但是我们并没有选择它。我们能够预料到会花费大量的时间在避免内存问题或者数据竞争问题上。并且
C++ 没有官方包管理器,这让维护和编译第三方依赖变得异常麻烦和困难,进而导致很长的研发周期。

为什么不是 Go ?

一开始我们考虑的是使用 Go ,但是接着就放弃了这个想法。Go 的 GC 能修复很多内存问题,但是有时仍然会停止运行中的进程。无论这个停止的时间有多短,我们都不能承受的起。Go 也没有解决数据竞争问题。即使我们在测试或运行时使用两次data -race 进行检测(注:go 语言可以用 -race 命令行参数检测数据竞争),也仍然不够。

另外,尽管我们可以使用 Goroutine 很容易写出并发逻辑,我们仍然不能忽视调度器的运行时开销。几天前我们遇到一个问题:我们使用多个 Goroutine 共享同一个
Context,却发现性能很糟糕,所以我们不得不让一个 Goroutine 使用一个子
Context,然后性能才变好点。

严格来说,CGO 有很严重的性能开销,但是我们必须无延迟地调用 RocksDB 的
API。基于以上原因,我们没有选择 Go,即使 Go 是我们团队最喜爱的语言。

所以,我们转向了 Rust...

但是 Rust...

Rust 是一门系统编程语言,由 Mozilla 在维护。它是一门非常强大的语言,但是你可以看看下面的曲线图,Rust 的学习曲线非常陡峭。


我用过很多编程语言,像 C++、Go、Python、Lua 等等,Rust 对我来说是最难掌握的一门语言。在 PingCAP,我们会让新同事至少花一个月的时间来学习 Rust,先和编译错误做斗争,然后再提高 Rust 水平。这个在学习 Go 的过程中从来没发生过。

此外,Rust 的编译时间很长,比 C++ 都长。每次当我输完 cargo build 命令开始构建 TiKV 的时候,我就可以做好多个俯卧撑了。

尽管 Rust 出来很长一段时间了,但是仍然缺乏很多库和工具,有些第三方项目至今还没有在生产环境中验证过。使用 Rust 对我们来说有很大的风险。最为严重的是,我们很难找到 Rust 程序员,因为在中国知道 Rust 的人寥寥无几,我们很缺人手。

然后,为什么还是选择 Rust ?

尽管 Rust 有以上种种缺点,但是它的好处深深地吸引我们。Rust 是内存安全的,我们再也不必担心内存泄漏和野指针问题。

Rust 是线程安全的,所以也没有任何数据竞争问题。所有的安全都是由编译器保证的。所以在大多少情况下,编译一旦通过,我们确信程序就能安全地运行。

Rust 没有 GC 开销,所以我们不会遇到 “stop the world” 问题。通过 FFI 调用 C 程序是非常快的,所以我们不担心调用 RocksDB API 会有性能上的降低。最后,Rust
有个官方的包管理器 crate ,我们找到很多库可以直接拿来用。

我们做了一个艰难而伟大的决定:使用 Rust!

TiKV 时间线


这里你可以看到 TiKV 的时间线。我们在 2016 年1月1号开始开发 TiKV,然后在2016年4月1号开源了,就像 Gmail 在每个4月1号愚人节那样做的,这并不是一个玩笑。2016年10月份,TiKV 第一次被使用在生产环境,那时我们甚至都还没有发布
beta 版。2016年11月,我们发布了第一个 beta 版;接着2016年12月发布 RC1 版本,今年2月份发布 RC2 版本。稍后我们计划4月份发布 RC3 版本,在6月份发布第一个 GA 版本。

如你所见,TiKV 的开发非常快,发布的版本都很稳定。选择 Rust 已经被证明是一个正确的决定。感谢 Rust。

TiKV 架构


现在让我们深入探究 TiKV。你可以从 TiKV 的架构中看到 TiKV 的层次很清晰,很容易理解。

TiKV 在底层使用了 RocksDB,一种高性能的持久化 Key-Value 存储,来作为后端的存储引擎。

接下来一层是 Raft KV 层。TiKV 使用 Raft 算法来实现异地复制数据。TiKV 被设计来存储海量数据,一个 Raft 组远不够。所以我们按范围切分,然后把每个范围的数据作为一个独立的 Raft Group。我们把这种方式命名为 Multi-Raft Groups。

TiKV 提供了一个简单的 Key-Value API,包括 SET、GET、DELETE 等方法,我们可以像使用其他任何分布式 Key-Value 存储系统那样使用 TiKV 的 API。TiKV 的上层也使用这些 API 来支持更高级的功能。

在 Raft 层之上是 MVCC。TiKV 中保存的所有 Key 必须包含一个全局唯一的时间戳,这个时间戳由 Placement Driver 来分配。TiKV 使用这个时间戳来支持分布式事务。

在最上层,是 KV 和协处理器 API 层,用来处理客户端的请求。

Multi-Raft

这是一个 Multi-Raft 的例子。

你可以看到有4个 TiKV 节点。在每一个节点存储中,我们有多个 Region。Region 是数据迁移和 Raft 复制的基本单元。每一个 Region 会被复制到三个节点中去。一个 Region 的三个副本组成了一个 Raft Group。

水平扩展</扩展>

水平扩展(初始状态)

这是一个水平扩展的例子。首先,我们有四个节点,节点A有3个 Region,其他节点都是2个 Region。

当然了,节点A比其他节点更忙,我们想减轻一下它的压力。

水平扩展(添加新节点)


我们添加一个新的节点 E,开始把节点 A 中的 Region 1 转移到节点 E。但是我们发现 Region 1 的 Leader 在节点 A 中,所以我们首先把 Leader 从节点 A 转移到节点B。

水平扩展(均衡)

之后 Region 1 的 Leader 在节点B中,然后我们在节点 E 中添加一个 Region 1 的副本。


然后我们从节点 A 中移除 Region 1 的副本。所有这一切都是被 Placement Driver 自动执行的。我们唯一要做的就是,如果发现系统繁忙就添加节点。非常简单,不是吗?

一个简单的写入流


这是一个简单的写入流:当客户端发送一个写请求给 TiKV,TiKV 首先解析协议,然后把请求分发给 KV 线程,KV 线程执行一些事务逻辑并把请求发送给 Raft 线程,在TiKV 复制 Raft Log 并且应用到 RocksDB 之后,整个写请求就结束了。

关键技术

现在,让我们关注一些核心技术。

关于网络层,我们使用了一个已经被广泛使用的协议 Protocol Buffers 来快速地对数据进行序列化和反序列化。

首先,我们使用** MIO **来构建网络框架。尽管 MIO 封装了底层的网络操作,但是它仍然是非常基础的库,我们必须手动地接受和发送数据,来解码或者编码我们自定义的网络协议。事实上很不方便。所以从 RC2 版本之后,我们已经在用 gPRC 来重构网络层了。gRPC 的优势非常明显。我们再也不用关心如何处理网络,只关系我们自己的逻辑,代码也看起来简单清晰。同时,用户可以很容易地使用其他编程语言来构建 TiKV 的客户端。我们已经在开发 TiKV 的 Java 客户端。

关于** 异步框架 **。接收到请求之后,TiKV 把请求分发给不同的线程来异步处理。一开始我们使用 MIO 加回调来处理异步请求,但是回调会中断代码逻辑,很难正确地阅读和写好代码,所以我们使用 tokio-core 和 futures 来重构代码,我们认为这是一种更为现代化的风格,对于未来的 Rust 来说。有时,我们也用线程池来分发简单的任务,以后会用 futures-cpupool。

关于** 存储 **,我们使用 rust-rocksdb 来访问 RocksDB。

关于** 监控 **,我们写了个 Prometheus 的 Rust 客户端,而且这个客户端在官方
wiki 上被推荐。关于性能监测,我们使用开启了监测功能的 jemallocator,用 clippy
来监测我们的代码。

未来计划

Ok, 以上就是我们已经做的和正在做的事情。我们未来将会做的事情有:

  • 让 TiKV 更快,比如移除 Box。我们为了写代码容易,在 TiKV 中使用了大量的 box
    技术,事实上一点也不高效。我们做性能测试发现,动态分发比静态分发至少慢3倍,以后我们会直接使用 Trait。
  • 让 TiKV 更稳定,比如引入 Rust sanitizer。
  • 贡献更多的 Rust 开源模块,比如 Raft 库,open-tracing 等等
  • 更加深度地参与其他 Rust 项目,比如 rust-gRPC
  • 在中国的社交媒体上写更多关于 Rust 的文章,组织更多的 Rust 聚会
  • 成为中国强有力的 Rust 倡导者

阅读原文

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • 寻找一种易于理解的一致性算法(扩展版) 摘要 Raft 是一种为了管理复制日志的一致性算法。它提供了和 Paxos...
    枝叶君阅读 2,632评论 0 15
  • 寻找一种易于理解的一致性算法(扩展版) 摘要 Raft 是一种为了管理复制日志的一致性算法。它提供了和 Paxos...
    yflau阅读 942评论 0 1
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,600评论 18 139
  • 你是我们家族的长子,不会害你的啊! 每每家里人对说这句话,我都压力巨大。 尤其是前半句,深怕做了什么错...
    GuXiZhouYiL阅读 289评论 3 3
  • 人生中什么最重要?——选择最重要! 是的,我们的人生在一次次的选择中造就了今天的模样。小到我今天早餐吃包子还是油条...
    慧说早安阅读 674评论 0 0