Abase2:字节跳动新一代高可用 NoSQL 数据库

作者 | NoSQL team

背景

自 2016 年以来,为了支撑在线推荐的存储需求而诞生的——字节跳动自研高可用 KV 存储
Abase,逐步发展成支撑包括推荐、广告、搜索、抖音、西瓜、飞书、游戏等公司内几乎所有业务线的 90% 以上的 KV
存储场景,已成为公司内使用最广泛的在线存储系统之一。

Abase 作为一款由字节跳动完全自研的高性能、大容量、高可用的 KV
存储系统,支撑了业务不断快速增长的需求。但随着公司的持续发展,业务数量、规模持续快速增长,我们业务对系统也提出了更高的要求,比如:

极致高可用:相对于一致性,信息流等业务对可用性要求更高,希望消除宕机选主造成的短时间不可用,和慢节点问题;

全球部署:无论是边缘机房还是不同地域的机房,同一个 Abase2 集群的用户都可以就近访问,获取极快的响应延迟;

CRDT 支持:确保多写架构下的数据能自动解决冲突问题,达成最终一致;

更低成本:通过资源池化解决不同用户资源使用不均衡,造成资源利用率不足问题,降低成本;

极致高性能:相同的资源使用下,要求提供尽可能高的写/读吞吐,和较低的访问延迟。适配 IO 设备和 CPU 性能发展速度不匹配趋势,极致高效对 CPU
的使用;

兼容 Redis 协议:为了让 Redis 用户可以无障碍的接入 Abase,以满足更大容量的存储需求,我们需要完全兼容 Redis 协议。

在此背景下,Abase 团队于 2019 年年底开始孵化第二代 Abase
系统。结合业界的先进架构方案及公司内部实践过程中的积累和思考,团队推出了资源池化,支持多租户、多写、CRDT 的软硬件一体化设计的新一代 NoSQL 数据库
—— Abase2。

架构概览

数据模型

Abase 支持 Redis 的几种主要数据结构与相应接口:

String: 支持 Set、Append、IncrBy,是字节线上使用最为广泛的数据模型;

Hash/Set:使用率仅次于 String,在部分更新/查询的结构化数据存取场景中广泛使用;

ZSet: 广泛应用于榜单拉链等在线业务场景,区别于直接使用 String Scan 方式进行包装,Abase 在 ZSet
结构中做了大量优化,从设计上避免了大量 ZIncrBy 造成的读性能退化;

List/TTLQueue: 队列接口语义使业务在对应场景下非常方便地接入。

架构视图

Abase2:字节跳动新一代高可用 NoSQL 数据库插图亿华云

图 1:Abase2 整体架构图

Abase2 的整体架构主要如上图所示,在用户、管控面、数据面三种视角下主要包含 5 组核心模块。

RootServer

线上一个集群的规模大约为数千台机器,为管理各个集群,我们研发了 RootServer 这个轻量级组件。顾名思义,RootServer
拥有全集群视角,它可以更好地协调各个集群之间的资源配比,支持租户在不同集群之间的数据迁移,提供容灾视图并合理控制爆炸半径。

MetaServer

Abase2 是多租户中心化架构,而 MetaServer 则是整个架构的总管理员,它主要包括以下核心功能:

管理元信息的逻辑视图:包括 Namespace,Table,Partition,Replica 等状态和配置信息以及之间的关系;

管理元信息的物理视图:包括 IDC,Pod,Rack,DataNode,Disk,Core 的分布和 Replica 的位置关系;

多租户 QoS 总控,在异构机器的场景下根据各个租户与机器的负载进行副本 Balance 调度;

故障检测,节点的生命管理,数据可靠性跟踪,在此基础上进行节点的下线和数据修复。

Abase2:字节跳动新一代高可用 NoSQL 数据库插图1亿华云

图 2: 集群物理视图

Abase2:字节跳动新一代高可用 NoSQL 数据库插图2亿华云

图 3: 集群逻辑视图

DataNode

DataNode 是数据存储节点。部署时,可以每台机器或者每块盘部署一个 DataNode,为方便隔离磁盘故障,线上实际采用每块盘部署一个
DataNode 的方式。

DataNode 的最小资源单位是 CPU Core(后简称 Core),每个 Core 都拥有一个独立的 Busy Polling 协程框架,多个
Core 共享一块盘的空间与 IO 资源。

Abase2:字节跳动新一代高可用 NoSQL 数据库插图3亿华云

图 4:DataNode 资源视角

一个 Core 包含多个 Replica,每个 Replica 的请求只会在一个 Core 上
Run-to-Complete,可以有效地避免传统多线程模式中上下文切换带来的性能损耗。

Replica 核心模块如下图所示,整个 Partition 为 3 层结构:

数据模型层:如上文提到的 String, Hash 等 Redis 生态中的各类数据结构接口。

一致性协议层:在多主架构下,多点写入势必会造成数据不一致,Anti-Entropy
一方面会及时合并冲突,另一方面将协调冲突合并后的数据下刷至引擎持久化层并协调 WAL GC。

数据引擎层:数据引擎层首先有一层轻量级数据暂存层(或称 Conflict
Resolver)用于存储未达成一致的数据;下层为数据数据引擎持久化层,为满足不同用户多样性需求,Abase2
引设计上采用引擎可插拔模式。对于有顺序要求的用户可以采用 RocksDB,TerarkDB 这类 LSM 引擎,对于无顺序要求点查类用户采用延迟更稳定的 LSH
引擎。

Abase2:字节跳动新一代高可用 NoSQL 数据库插图4亿华云

图 5: Replica 分层架构

Client/Proxy/SDK

Client 模块是用户侧视角下的核心组件,向上提供各类数据结构的接口,向下一方面通过 MetaSync 与 MetaServer 节点通信获取租户
Partition 的路由信息,另一方面通过路由信息与存储节点 DataNode 进行数据交互。此外,为了进一步提高服务质量,我们在 Client 的 IO
链路上集成了重试、Backup Request、热 Key 承载、流控、鉴权等重要 QoS 功能。

结合字节各类编程语言生态丰富的现状,团队基于 Client 封装了 Proxy 组件,对外提供 Redis 协议(RESP2)与 Thrift
协议,用户可根据自身偏好选择接入方式。此外,为了满足对延迟更敏感的重度用户,我们也提供了重型 SDK 来跳过 Proxy 层,它是 Client
的简单封装。

DTS (Data Transfer Service)

DTS 主导了 Abase
生态系统的发展,在一二代透明迁移、备份回滚、Dump、订阅等诸多业务场景中起到了非常核心的作用,由于篇幅限制,本文不做更多的详细设计叙述。

关键技术

一致性策略

我们知道,分布式系统难以同时满足强一致性、高可用性和正确处理网络故障(CAP
)这三种特性,因此系统设计者们不得不做出权衡,以牺牲某些特性来满足系统主要需求和目标。比如大多数数据库系统都采用牺牲极端情况下系统可用性的方式来满足数据更高的一致性和可靠性需求。

Abase2 目前支持两种同步协议来支持不同一致性的需求:

多主模式(Multi-Leader):相对于数据强一致性,Abase 的大多数使用者们则对系统可用性有着更高的需求,Abase2
主要通过多主技术实现系统高可用目标。在多主模式下,分片的任一副本都可以接受和处理读写请求,以确保分片只要有任一副本存活,即可对外提供服务。同时,为了避免多主架构按序同步带来的一些可用性降低问题,
我们结合了无主架构的优势,在网络分区、进程重启等异常恢复后,并发同步最新数据和老数据。此外,对于既要求写成功的数据要立即读到,又不能容忍主从切换带来的秒级别不可用的用户,我们提供无更新场景下的写后读一致性给用户进行选择。实现方式是通过
Client 配置 Quorum 读写(W R

THE END
Copyright © 2024 亿华云