本文选自“字节跳动基础架构实践”系列文章。
“字节跳动基础架构实践”系列文章是由字节跳动基础架构部门各技术团队及专家倾力打造的技术干货内容,和大家分享团队在基础架构发展和演进过程中的实践经验与教训,与各位技术同学一起交流成长。
字节跳动存在海量结构化与半结构化数据的诸多存储需求。本文将对字节跳动分布式表格存储系统的演进进行深入剖析,展示我们如何从解决业务需求开始一步一个脚印的朝着业界顶尖的目标靠近。
概要
分布式表格存储系统在业界拥有广泛的应用场景。Google 先后发布了 Bigtable 和 Spanner 两代分布式表格存储系统,承接了其公司内部和外部云服务中的所有表格存储需求。其中 Bigtable 的开源实现 HBase 在国内外公司中都得到了广泛的使用,并且开源的图数据库 JanusGraph 、时序数据库 OpenTSDB 、地理信息数据库 GeoMesa、关系型数据库 Phoenix 等底层都是基于 HBase 进行数据存储的。字节跳动也大量使用 HBase 作为表格存储服务,HBase 在字节跳动的实践过程中,在极大数据量和极高负载的场景下存在延时长尾较高和可用性不足的问题。为此字节跳动表格存储团队经过大量开源系统调研并最终决定研发一套兼容 HBase 数据模型和语义的高可用、强一致、低延时、高吞吐、全局有序且对 SSD 和 HDD 盘同时友好的分布式表格存储系统,以应对字节跳动业务迅猛发展带来的技术挑战。目前第一代表格存储系统 Bytable1.0 已作为搜索和推荐的底层数据存储稳定运行,我们团队正在此基础上演进第二代表格存储系统 Bytable2.0。
1. 背景
随着头条全网搜索项目的启动和发展,业务需要一个全局有序、容量巨大同时性能高效的表格存储系统以存储整个互联网中所有链接和网页,并保证互联网上发生的所有变更都被能实时的更新到表格存储系统中。在此之前,搜索引擎技术的领导者 Google 为此构建了 Bigtable ,并在其搜索、地球和财经等多个项目中使用。其中搜索业务在其发表的论文中做了主要论述,因而之后很多运营搜索业务的公司都效仿其使用方式。我们团队最初使用 Bigtable 的开源实现 HBase 为搜索业务提供服务。在全网链接关系的实时更新需求下需要提供足够高的可用性和足够低的延时,由于其数据量极其庞大所以会创建极多的数据分片,集群的整体尾延时和可用性会随着数据分片实例数的增多而成指数级别的恶化,因此对每一个分片实例的延时和可用性提出了更高的要求。但由于 HBase 存在尾延时较高和可用性较低的问题,并不能满足我们的需求,于是我们团队开始了调研和技术选型。

2. Bytable1.0 应运而生的第一代表格存储系统
2.1 技术选型
在我们的需求下,面对海量的网页链接和网页数据,数据量非常庞大,全部使用 SSD 盘将极大地增加存储成本,所以我们面向的设备不能只考虑 SSD 盘,需要在 HDD 盘上也提供高效的支持。目前广泛使用的全局有序的开源分布式表格存储系统有 HBase, TiDB, CockroachDB 等。经过验证 HBase 由于其延时长尾和可用性问题满足不了我们的需求。TiDB 和 CockroachDB 在我们的需求下存在:(1)数据迁移时需要多路归并扫描数据;(2)写数据时需要两份日志;这两个引起磁头摇摆对 HDD 盘的不友好的问题。由于这些系统都不能解决我们的问题,最终我们团队决定结合目前公司提供的软硬件资源水平和业务场景,独立研发一套兼容 HBase 数据模型和语义的高可用、强一致、低延时、高吞吐、全局有序且对 SSD 和 HDD 盘同时友好的分布式表格存储系统 Bytable1.0。在设计方案时,我们团队遵循的原则是在满足业务需求的前提下,尽可能的简化设计。业界的分布式表格存储系统往往使用共享存储架构,但在当时公司内部的分布式文件存储系统不能提供足够高的可用性和 SLA。为了满足业务的高可用需求,我们决定不依赖分布式文件存储系统直接管理本地磁盘。
2.2 系统架构
下面我们来介绍 Bytable1.0 的系统架构,如下图所示主要由 Master, PlacementDriver 和 TabletServer 三个模块组成。Client 会跟 Master 通信取得 Tablet (与 Bigtable 类似,使用 Tablet 表示表中的一个数据分片,表示表中的一个 KeyRange 对应的部分数据) 的元信息,以得到对应 Tablet 所在的 TabletServer 的地址,然后 Client 跟对应的 TabletServer 进行通信进行数据读写。类似于 HDFS 的 NameNode 和 DataNode 分别处理控制平面和数据平面的任务,我们将整个 Bytable1.0 划分了控制平面(Master),决策平面(Placement Driver)和数据平面(TabletServer),我们将详细介绍每一个平面的内部设计。

2.2.1 控制平面 Master
Master 控制了 Tablet 主动切主、被动选主、迁移、分裂与合并的具体流程控制,此外 Master 向 Client 暴露 Tablet 分布的位置信息。这里以被动切主为例介绍下大致流程,当 Master 在指定时间内没有收到某 TabletServer 的心跳消息,则将此 TabletServer 判定为 Offline,然后对此 TabletServer 上为 Leader 的 Tablet 发起一轮选主。选主的过程与 Raft 类似仅仅是将这部分逻辑上移到 Master 统一管理,首先收集 Tablet 各个副本的最新日志号,然后选取足够新的副本作为选主目标,向 Tablet 各个副本发起投票,当投票成功后向选主目标发送当主请求。因为 Master 接管了控制平面的所有工作,所以逻辑和 RPC 交互会非常复杂,并且因为对性能没有特别高的要求,所以选择了 Go 语言进行开发,以降低研发成本,提升研发效率。
2.2.2 决策平面 Placement Driver
Master 控制了指定操作的具体流程,但是不对是否发起指定操作(除了被动选主)做决策,而这个决策的任务由 PlacementDriver 来负责。它会扫描 Master 获得分区的负载信息,经过各种策略的计算生成决策信息,发送给 Master 实际执行。因为策略的更新往往更频繁,拆成两个模块的话策略的更新不需要滚动重启 Master,对用户来说可以做到完全无感知。为了降低复杂策略的编写成本,PD 也选择了 Go 语言进行开发。
2.2.3 数据平面 TabletServer
TabletServer 承载了多个 Tablet 的数据存储,并负责进行实际数据的读写操作。每一个 Tablet 都会与其