本系列文章主要面向 TiKV 社区开发者,重点介绍 TiKV 的系统架构,源码结构,流程解析。目的是使得开发者阅读之后,能对 TiKV 项目有一个初步了解,更好的参与进入 TiKV 的开发中。本文是本系列文章的第六章节。重点介绍 TiKV 中 Raft 的优化。

在分布式领域,为了保证数据的一致性,通常都会使用 Paxos 或者 Raft 来实现。但 Paxos 以其复杂难懂著称,相反 Raft 则是非常简单易懂,所以现在很多新兴的数据库都采用 Raft 作为其底层一致性算法,包括我们的 TiKV。

当然,Raft 虽然简单,但如果单纯的按照 Paper 的方式去实现,性能是不够的。所以还需要做很多的优化措施。本文假定用户已经熟悉并了解过 Raft 算法,所以对 Raft 不会做过多说明。(还不熟悉 Raft,点这里:)TiKV 源码解析系列——如何使用 Raft

Simple Request Flow

这里首先介绍一下一次简单的 Raft 流程:

1. Leader 收到 client 发送的 request。

2. Leader 将 request append 到自己的 log。

3. Leader 将对应的 log entry 发送给其他的 follower。

4. Leader 等待 follower 的结果,如果大多数节点提交了这个 log,则 apply。

5. Leader 将结果返回给 client。

6. Leader 继续处理下一次 request。

可以看到,上面的流程是一个典型的顺序操作,如果真的按照这样的方式来写,那性能是完全不行的。:P

Batch and Pipeline

首先可以做的就是 batch,大家知道,在很多情况下面,使用 batch 能明显提升性能,譬如对于 RocksDB 的写入来说,我们通常不会每次写入一个值,而是会用一个 WriteBatch 缓存一批修改,然后在整个写入。 对于 Raft 来说,Leader 可以一次收集多个 requests,然后一批发送给 Follower。当然,我们也需要有一个最大发送 size 来限制每次最多可以发送多少数据。

如果只是用 batch,Leader  还是需要等待 Follower 返回才能继续后面的流程,我们这里还可以使用 Pipeline 来进行加速。大家知道,Leader 会维护一个 NextIndex 的变量来表示下一个给 Follower 发送的 log 位置,通常情况下面,只要 Leader 跟 Follower 建立起了连接,我们都会认为网络是稳定互通的。所以当 Leader 给 Follower 发送了一批 log 之后,它可以直接更新 NextIndex,并且立刻发送后面的 log,不需要等待 Follower 的返回。如果网络出现了错误,或者 Follower 返回一些错误,Leader 就需要重新调整 NextIndex,然后重新发送 log 了。

Append Log Parallelly

对于上面提到的一次 request 简易 Raft 流程来说,我们可以将 2 和 3 并行处理,也就是 Leader 可以先并行的将 log 发送给 Followers,然后再将 log append。为什么可以这么做,主要是因为在 Raft 里面,如果一个 log 被大多数的节点 append,我们就可以认为这个 log 是被 committed 了,所以即使 Leader 再给 Follower 发送 log 之后,自己 append log 失败 panic 了,只要 `N / 2 + 1` 个 Follower 能接收到这个 log 并成功 append,我们仍然可以认为这个 log 是被 committed 了,被 committed 的 log 后续就一定能被成功 apply。

那为什么我们要这么做呢?主要是因为 append log 会涉及到落盘,有开销,所以我们完全可以在 Leader 落盘的同时让 Follower 也尽快的收到 log 并 append。

这里我们还需要注意,虽然 Leader 能在 append log 之前给 Follower 发 log,但是 Follower 却不能在 append log 之前告诉 Leader 已经成功 append 这个 log。如果 Follower 提前告诉 Leader 说已经成功 append,但实际后面 append log 的时候失败了,Leader 仍然会认为这个 log 是被 committed 了,这样系统就有丢失数据的风险了。

Asynchronous Apply

上面提到,当一个 log 被大部分节点 append 之后,我们就可以认为这个 log 被 committed 了,被 committed 的 log 在什么时候被 apply 都不会再影响数据的一致性。所以当一个 log 被 committed 之后,我们可以用另一个线程去异步的 apply 这个 log。

所以整个 Raft 流程就可以变成:

1. Leader 接受一个 client 发送的 request。

2. Leader 将对应的 log 发送给其他 follower 并本地 append。

3. Leader 继续接受其他 client 的 requests,持续进行步骤 2。

4. Leader 发现 log 已经被 committed,在另一个线程 apply。

5. Leader 异步 apply log 之后,返回结果给对应的 client。

使用 asychronous apply 的好处在于我们现在可以完全的并行处理 append log 和 apply log,虽然对于一个 client 来说,它的一次 request 仍然要走完完整的 Raft 流程,但对于多个 clients 来说,整体的并发和吞吐量是上去了。

Now Doing…

→ST Snapshot

在 Raft 里面,如果 Follower 落后 Leader 太多,Leader 就可能会给 Follower 直接发送 snapshot。在 TiKV,PD 也有时候会直接将一个 Raft Group 里面的一些副本调度到其他机器上面。上面这些都会涉及到 Snapshot 的处理。

在现在的实现中,一个 Snapshot 流程是这样的:

1. Leader scan 一个 region 的所有数据,生成一个 snapshot file。

2. Leader 发送 snapshot file 给 Follower。

3. Follower 接受到 snapshot file,读取,并且分批次的写入到 RocksDB。

如果一个节点上面同时有多个 Raft Group 的 Follower 在处理 snapshot file,RocksDB 的写入压力会非常的大,然后极易引起 RocksDB 因为 compaction 处理不过来导致的整体写入 slow 或者 stall。

幸运的是,RocksDB 提供了[SST]机制,我们可以直接生成一个 SST 的 snapshot file,然后 Follower 通过 injest 接口直接将 SST file load 进入 RocksDB。

→Asynchronous  Lease Read

在之前的 [Lease Read] TiKV 源码解析系列 - Lease Read 文章中,我提到过 TiKV 使用 ReadIndex 和 Lease Read 优化了 Raft Read 操作,但这两个操作现在仍然是在 Raft 自己线程里面处理的,也就是跟 Raft 的 append log 流程在一个线程。无论 append log 写入 RocksDB 有多么的快,这个流程仍然会 delay Lease Read 操作。

所以现阶段我们正在做的一个比较大的优化就是在另一个线程异步实现 Lease Read。也就是我们会将 Leader Lease 的判断移到另一个线程异步进行,Raft 这边的线程会定期的通过消息去更新 Lease,这样我们就能保证 Raft 的 write 流程不会影响到 read。

总结

虽然外面有声音说 Raft 性能不好,但既然我们选择了 Raft,所以就需要对它持续的进行优化。而且现阶段看起来,成果还是很不错的。相比于 RC1,最近发布的 RC2 无论在读写性能上面,性能都有了极大的提升。但我们知道,后面还有很多困难和挑战在等着我们,同时我们也急需在性能优化上面有经验的大牛过来帮助我们一起改进。

如果你对我们做的东西感兴趣,想让 Raft 快的飞起,欢迎联系我们:邮箱:info@pingcap.com,当然,你也可以添加本期作者唐刘的个人微信:siddontang

延展阅读:

TiKV 源码解析系列 - Lease Read

TiKV 源码解析系列 - PD Scheduler

TiKV 源码解析系列——Placement Driver

TiKV 源码解析系列——multi-raft 设计与实现

TiKV 源码解析系列——如何使用 Raft

TiKV 源码解析系列 - Raft 的优化的更多相关文章

  1. TiKV 源码解析系列文章(三)Prometheus(上)

    本文为 TiKV 源码解析系列的第三篇,继续为大家介绍 TiKV 依赖的周边库 rust-prometheus,本篇主要介绍基础知识以及最基本的几个指标的内部工作机制,下篇会介绍一些高级功能的实现原理 ...

  2. TiKV 源码解析系列——如何使用 Raft

    本系列文章主要面向 TiKV 社区开发者,重点介绍 TiKV 的系统架构,源码结构,流程解析.目的是使得开发者阅读之后,能对 TiKV 项目有一个初步了解,更好的参与进入 TiKV 的开发中. 需要注 ...

  3. TiKV 源码解析系列——Placement Driver

    https://zhuanlan.zhihu.com/p/24809131?refer=newsql

  4. 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新

    [原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...

  5. 【原】Android热更新开源项目Tinker源码解析系列之三:so热更新

    本系列将从以下三个方面对Tinker进行源码解析: Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Android热更新开源项目Tinker源码解析系列之二:资源文件热更新 A ...

  6. 【原】Android热更新开源项目Tinker源码解析系列之二:资源文件热更新

    上一篇文章介绍了Dex文件的热更新流程,本文将会分析Tinker中对资源文件的热更新流程. 同Dex,资源文件的热更新同样包括三个部分:资源补丁生成,资源补丁合成及资源补丁加载. 本系列将从以下三个方 ...

  7. Cwinux源码解析系列

      Cwinux源码解析系列

  8. 【安卓网络请求开源框架Volley源码解析系列】定制自己的Request请求及Volley框架源码剖析

    通过前面的学习我们已经掌握了Volley的基本用法,没看过的建议大家先去阅读我的博文[安卓网络请求开源框架Volley源码解析系列]初识Volley及其基本用法.如StringRequest用来请求一 ...

  9. Android源码解析系列

    转载请标明出处:一片枫叶的专栏 知乎上看了一篇非常不错的博文:有没有必要阅读Android源码 看完之后痛定思过,平时所学往往是知其然然不知其所以然,所以为了更好的深入Android体系,决定学习an ...

随机推荐

  1. haproxy 关闭ssl3

    在使用的过程中,我们发现在用IE浏览器的时候 我们设置成 然后我们的网站会出现部分页面不出来 然后我们也发现在扫面网站链接的时候 所以当务之急是关闭ssl3 我这里的反向代理使用的是haproxy 版 ...

  2. SVN配置钩子文件限制提交文件时必须填写更新日志

    进入相应SVN仓库hooks目录,编辑文件pre-commit #!/bin/sh REPOS="$1"TXN="$2" SVNLOOK=/usr/bin/sv ...

  3. IOS 缓存方案(按需缓存 、 预缓存)及 低网速模拟

    1,在设备中 设置开发者模式. 参照上面设置 自定义 添加.丢包率 35. 或者参照这个文章:http://ivoryxiong.org/devops/2013/05/24/ios_dev_handl ...

  4. Java:多线程,Exchanger同步器

    1. 背景 类java.util.concurrent.Exchanger提供了一个同步点,在这个同步点,一对线程可以交换数据.每个线程通过exchange()方法的入口提供数据给他的伙伴线程,并接收 ...

  5. RVM切换ruby版本号

    RVM是Ruby Version Manager的缩写,是一个命令行工具,它能够让你轻松地安装,管理和使用多个版本号的Ruby.不同的rails项目使用等ruby和rails版本号不一样的时候.能够使 ...

  6. Hibernate 建立一对多双向关联关系

    下面内容整理自<精通Hibernate>第二版 注:既然是双向关联."一对多双向关联"和"多对一双向关联"是同一回事. 对象位于内存中,在内存中从一 ...

  7. GGGGCCCC

    Evaluating and improving remembered sets in the HotSpot G1 garbage collector http://www.diva-portal. ...

  8. 每天一个linux命令(7):whereis 命令

    whereis命令只能用于程序名的搜索,而且只搜索二进制文件(参数-b).man说明文件(参数-m)和源代码文件(参数-s).如果省略参数,则返回所有信息. 和find相比,whereis查找的速度非 ...

  9. Eclipse工程文件夹 红叹号

    问题产生: 重装系统后,换了个新版本的Eclipse,但是,使用以前的工程目录(工程目录重命名了)后,发现,一些工程上显示红叹号,仔细查看里面的代码,也没有错误 问题原因: 经过查看,是工程中,使用的 ...

  10. grpc-golang实现账号and密码认证

    // I would recommend to use interceptors: // client grpc.Dial(target, grpc.WithPerRPCCredentials(&am ...