字节跳动技术团队

字节KV数据库(ABase)架构论文入选数据库顶会SIGMOD 2025

Image

导读

SIGMOD (ACM Special Interest Group on Management Of Data) 是数据库三大国际顶级学术会议之一,也是数据库领域影响力最大的顶级会议,中国计算机学会 (CCF) 推荐的 A 类国际学术会议。今年的 SIGMOD 于 6 月 22 日到 6 月 27 日,在德国柏林举行。其中来自字节跳动的 KV 数据库 (ABase) 架构论文,被 SIGMOD2025 收录为 Industry Track 论文。论文由字节 RedisFamily 团队和 ByteBrain 团队合作完成。

论文标题: ABase: the Multi-Tenant NoSQL Serverless Database for Diverse and Dynamic Workloads in Large-scale Cloud Environments

论文作者: Rong Kang ; Yanbin Chen; Ye Liu; Fuxin Jiang; Qingshuo Li; Miao Ma; Jian Liu*; Guangliang Zhao ; Tieying Zhang; Jianjun Chen; Lei Zhang 全部来自字节跳动,第一作者 Rong Kang 来自字节跳动 ByteBrain 团队,通讯作者 Jian Liu 来自字节跳动 RedisFamily 团队。

论文地址: https://dl.acm.org/doi/10.1145/3722212.3724426

论文背景

时间回到 2016 年,字节跳动自研的高可用 KV 数据库 ABase 上线,以其极致的性能和易用性优势,在字节内部大受欢迎。然而,伴随着字节跳动业务的飞速发展,ABase 用户量也爆炸式的增长,初代 ABase 的单租户架构越来越难堪重负。在当时我们遇到了以下难题:

  • 「资源浪费:」 每个业务独占集群,资源不能共享,CPU / 磁盘资源大量闲置。
  • 「弹性不足:」 业务紧急扩容时,时间需要数小时乃至数天,用户体验断崖下跌。
  • 「运维噩梦:」 超多集群的各种超负载、慢节点等问题让运维团队疲于奔命。

ABase 就像一辆超载的马车,随时可能散架。团队夜以继日地优化,始终治标不治本。 在 2019 年,我们做了一个大胆的决定: 用多租户和无主架构重构 ABase,以期彻底解决资源、弹性和运维问题。经过多年不懈的坚持,我们大幅度改善了以上问题,将 ABase 资源利用率提升了 50% 以上。下面我们将为大家介绍一下这篇 ABase 论文的详细内容:

多租户 Serverless 的挑战

Serverless NoSQL 数据库已成为云原生的关键技术。它像水电一样按需供给,无需管理服务器,却能支撑起双十一的秒杀洪流、社交媒体的爆款话题,甚至 AI 场景的海量动态数据吞吐。而这一切的核心,离不开多租户架构——通过让不同租户共享同一资源池,最大化资源利用率。

亚马逊 DynamoDB、微软 CosmosDB 和谷歌 Firestore 等头部产品均已采用多租户设计。然而,资源利用率提升的同时,也带来了前所未有的技术挑战。

在字节跳动,ABase 支撑着抖音、电商、搜索、广告、推荐、飞书、豆包等多样化业务,管理着 「130亿峰值QPS」和「1EB数据容量」。其中单个租户的峰值 QPS 可达 4.5 亿,存储容量超 11PB。多租户 NoSQL 数据库支持如此极致的业务需求非常有挑战。我们总结了多租户架构的四大挑战,并在 ABase 设计中逐一解决:

产品线
工作负载
标准化吞吐
标准化存储
缓存命中率
读命令比例
平均 KV 大小
常用 TTL
Social Media(Douyin)
Comment
250
125
54%
100%
0.1KB
-
Social Media(Douyin)
Direct message
25
678
74%
100%
1KB
-
E-Commerce
Metadata tags
575
42
92%
100%
1KB
-
Search
Forward sorted data
1500
63
99%
100%
1KB
-
Advertisement
For message joiner
2750
938
18%
25%
10KB
3 hours
Recommendation
For deduplication
5325
625
76%
50%
2KB
15 days
Large Language Model
Remote KVCache
10000
5760
0%
85%
5MB
1 days

【表 1:ABase 不同业务场景的特点】

挑战 1:缓存机制对多租户系统性能隔离的影响

NoSQL 数据库中,缓存机制极其重要,是降低延迟、提升性能的关键。ABase 采用 Proxy 和 DataNode 两级缓存:命中时直接从内存返回数据 (仅消耗 CPU / 内存),未命中则需磁盘 I/O(延迟更高)。这种差异会干扰多租户隔离——比如一个租户的缓存失效可能突然抢占磁盘带宽,影响其他租户。同时传统单租户架构下也难以解决用户的缓存命中率下降,导致无法提供标称 QPS 能力的问题。

挑战 2 和 3:快速变化负载对多租户系统的冲击

在真实业务中,租户负载可能瞬间飙升或剧烈波动。以字节电商为例 (图 1):

Image
Image
Image

【图 1:字节电商业务双十一流量变化图】

如图所示:租户的流量模式会因为双十一而产生不同的变化。图 1(b) 的租户,由于所请求的 Key 的分布更加广泛导致缓存命中率显著下降超过 20%;图 1(c) 的租户,由于热 Key 场景,缓存命中率增加了 10%;图 1(e) 的租户,经历了持续约 3 天的流量峰值,并且缓存命中率从 100% 骤降为 2%。

这对多租户 NoSQL 数据库带来了两个方面的挑战:

  1. 随着租户流量的增加,预应用的资源配额 (称为 quota) 可能会耗尽,从而触发限制;相反,租户流量的减少通常会导致这些 quota 浪费(挑战 2);
  2. 即使租户流量不变,如果请求集中在少数 Key 上,可能会导致热 Key 压力;相反,如果请求分布广泛,可能会导致缓存命中率急剧下降 (挑战 3)。

挑战 4:多维度负载均衡

租户需求千差万别:有的高吞吐 (RU) 但存储小,有的海量存储但请求少,有的读写比例极端倾斜。若数据布局规划不当,会导致 CPU 或磁盘空间资源的闲置。

Image

【图 2:ABase 租户的RU、存储用量、读比率分布图】

系统设计

系统概要

ABase 支持 Redis 协议,以方便熟悉 Redis 的用户使用,并实现最终一致性。如图 3 所示,ABase 系统包括一系列资源池,每个资源池管理一组租户。租户可以创建多个 Key-Value Table,每个 Table 由多个 items 组成,每个 item 由唯一的 Key 来标识。属于同一个租户的数据会按照 Hash key 被统一分配到几个连续不相交的分片 (Partition) 中。每个 Partition 跨多个可用区域 (Availability Zones) 生成多个副本,增强可用性和健壮性。多租户共享一块资源池,可以利用工作负载多样性,让具备不同资源需求的租户共存,共享空闲资源,从而提高机器资源利用率和弹性。

Image

【图 3:ABase 多租户架构图】

如图 3 所示,ABase 由三部分组成:控制平面、数据平面和代理平面:

  • 控制平面:其中 Meta Server 负责管理集群元数据,监控集群健康状况等;Predictive Autoscaler 负责收集租户流量和存储利用率,根据这些信息做出扩缩容决策;Intra-Pool & Inter-Pool Rescheduler 根据负载信息在资源池内和资源池外进行资源调度;
  • 数据平面包含多个资源池,每个资源池中包含多个 DataNode。每个 DataNode 都分配一块物理磁盘和对应的 CPU 资源,并且管理不同租户的 Partition。DataNode 根据租户特定 Partition quota(Tenant-specific Partition Quota)来处理 Partition 层的流量控制,并且配备了一个大小感知的 LRU(Size-Aware LRU,SA-LRU)和细粒度权重公平队列 (Weighted Fair Queueing,WFQ) 模块,共同保证多租户环境中的服务质量(Quality of Service,QoS);
  • 代理平面由属于不同租户的 Proxy Groups 组成。每个租户独占一组 Proxy Group。收到用户客户端发出的请求后,Proxy 通过从 MetaServer 获取的集群元数据来路由请求到不同的 DataNode 中。Proxy 会根据每个租户的 Proxy qutoa 来进行代理层流量控制,并且配备了基于 active-update 的 LRU(AC-LRU)。

缓存感知的多租户性能隔离 (挑战 1)

为了应对挑战 1,ABase 需要找到一个缓存感知的归一化请求流量表示,通过一定的限制策略限制单租户的流量,并且考虑不同租户在同一台机器上的资源使用公平性。

我们首先设计了一个缓存感知的请求单元 (Request Unit,RU),将缓存命中率纳入 RU 计算。RU 广泛应用于 Serverless 数据库中,能够将底层硬件的资源消耗抽象出来,将用户的注意力专注到请求吞吐量需求。而在 ABase 中,RU 不仅对计费至关重要,还是多租户性能隔离的关键组件。下面介绍一下 ABase 如何针对不同的请求类型定制 RU 计算,以确保 RU 反映的是实际资源消耗,同时考虑缓存机制对资源消耗的影响。

首先定义一些变量:

  • Swrite:用户请求的 value size
  • U:unit 大小,通常为 2KB
  • r:ABase 中为这个租户设置的数据副本数量
  • E[Swrite]:最后 k 个读请求读取 value size 的移动平均值
  • E[Rhit]:最后 k 个读请求缓存命中率的移动平均值

「写操作」:总消耗为 r∗RUwrtie,其中 RUwrite=Swrite/U;

「读操作」:预估消耗为 RUread=E[Sread]∗(1−E[Rhit])/U。通过这个预估消耗进行流量控制,但最后计费会根据实际消耗来收费;

「Scan 操作」:每次 SCAN 如果不超过 10 个 key,并且请求结果不超过 2KB,会被统计为 1RU;如果 SCAN 超过了 10 个 key,则以 10 个 key 为 1RU,不足 10 个 Key 的,按 1RU 计算;如果 SCAN 请求超过 2KB,则以 2KB 为 1RU,不足 2KB 的按 1RU 算;

「复杂数据结构请求」:对于 ZSET/SET/HASH/LIST 等复杂数据结构,会先按照读写或 SCAN 类型的请求的 RU 计算逻辑先计算一下,然后再乘以对应命令的 RU 复杂度。

Image

【图 4:缓存感知性能隔离机制图】

「有了RU的计算和预估规则后,又该怎么实现请求限制,使请求不会超过每个租户的请求配额呢?」

如图 4 所示,我们实现了分层的请求限制策略,分为 Proxy 层和 Partition 层。

「在 Proxy 层」,Proxy 的主要职责是防止 RU 总数超过租户配额。ABase Proxy 采用了一种异步流量控制策略来减少 Proxy 和 MetaServer 之间的依赖。首先,每个 Proxy 都会定义一个特定的 Proxy_quota,该配额是简单地通过租户配额除以 Proxy 数量计算得到,同时允许它们自主处理该配额的两倍请求。除此之外,为了将租户在所有 Proxy 的总流量保持在租户配额内,MetaServer 会持续监控每个 Proxy 的流量,如果超过总数超过配额,则会通知 Proxy 恢复其标准的 proxy_quota。这种限速能够保护 DataNodes 上的租户免受 DataNode 上其他租户突发流量的影响。例如,当某些租户的流量显著升级时,该租户的 Proxy 可以拒绝请求,从而阻止请求到达 DataNode,减少 DataNode 处理或拒绝这些请求时的大量资源消耗,从而保护其他租户的稳定性。除此之外,Proxy 还会限制租户单个 Partition 的流量最高为 DataNode 的拒绝请求速率,防止 Proxy 放过的请求超过 DataNode 拒绝请求能力的上限 (DataNode 拒绝请求的能力比处理请求能力高,但是也有上限)。

「在Partition层」,DataNode 会给每个租户定义一个 Partition_quota,该配额通过租户配额除以分片数量得到。为了防止租户请求不均匀而导致的配额使用不完,DataNode 会确保单个分区的流量不会超过其 Partition_quota 的三倍,而不是一倍。

「现在,我们每个租户都能够合理地使用其配额不会超限,并且在 Proxy 层已经挡住了一部分租户的超额请求。但不可避免的 DataNode 会处理不同租户的请求,我们该怎么保证不同租户在 DataNode 中请求处理的公平性,同时又考虑到缓存机制对资源消耗的影响呢?」

我们设计了一种细粒度、双层加权公平队列 (Dual-Layer WFQ) 机制。首先,我们按照类型和大小把请求分成了四类,分别是 Large/Small Read/Write,并且为每一种类型的请求构建了 WFQ。除此之外,请求的资源消耗还取决于它们是否缓存命中。因此我们将 WFQ 分为了两层,分别是 CPU-WFQ 和 I/O-WFQ。请求首先进入上层 CPU-WFQ 进行处理。如果请求命中缓存,则可以直接返回;否则,则继续到 I/O-WFQ。

WFQ 以自定义的虚拟完成时间 (virtual finish time,VFT) 为比较对象构造最小堆,每次从 WFQ 中取出一个请求来处理。ABase 设计的 VFT 计算方法如下所示:

wReqCost(Qi)=Cost(Qi)/wPartition(Qi)

wPartition(Qi)=Qi/∑Qp

VFT(Qi)=preVFTTi+wReqCost(Qi)

wPartition(Qi) 表示该请求的配额占该 DataNode 所有租户的配额的比率。意味着如果它占比越大,优先级就越大。除此之外,统一租户的 VFT 计算累积的,从而防止单个租户的请求始终优先级较高。

我们在实践中规定了以下规则:

  1. CPU-WFQ 中的 Cost 基于 RU,而 I/O-WFQ 的成本由 IOPS 确定;
  2. 在 CPU-WFQ 中,我们首先保证单个租户的并发限制,避免单个租户占据所有并发,然后再对 DataNode 所有读写请求也执行并发限制,并且对于写操作,我们还对总 RU 设置上限,以确保底层存储引擎中 Compaction 和垃圾回收期间写延迟的稳定性;
  3. 单个租户的请求最多可以占用 90% 的 CPU-WFQ 资源,以防止单个租户的流量爆发期间其他租户的请求出现严重延迟;
  4. 在 I/O-WFQ 线程池中的所有基本线程都被一个租户的请求占用时,我们会临时增加额外的线程来处理其他租户的请求。该策略建立在 DataNode 上的两个租户不太可能同时出现高流量的基础上。

预测扩缩容 (挑战 2)

为了便于用户感知其所能使用到的容量上限,ABase 使用预置容量限制。我们希望能够识别出用户的非预期流量增长和预期内流量增长。对于非预期流量增长,我们依据预置容量限制做限速,便于用户准确控制成本上限。同时我们对于预期内的流量增长进行提前的资源扩容, 减少用户容量管理成本。

「我们是通过什么算法来预测未来的配额使用情况呢?」

我们开发了一种 ensemble-based forecasting solution。

在预处理阶段,我们使用多指标协作降低噪音。如果使用量和配额同时达到峰值,则会被视为噪音并过滤掉。因为在实践中,这两种情况同时发生几乎不可能。此外,我们使用启发式算法来消除零星峰值,这些峰值可能是由于偶然事件造成的。例如过去 10 天内只出现一次的事件。我们还利用变点检测来识别趋势变化,从而使预测算法更加关注最近的数据变化。

在预测阶段,我们首先使用功率谱密度 (Power Spectral Density,PSD) 分析来确定时间序列的周期性。随后,我们采用 Prophet 模型和历史平均算法得出加权预测集合。其中 Prophet 模型对具有明确趋势和周期的事件序列有效,而历史平均算法能提供稳定的预测,特别适合当趋势变化较小时。对于一致的非周期性爆发,如果预测明显低于历史输入数据,我们直接使用最近时期的历史数据进行预测,以避免不必要的规模缩减。

「完成了未来配额使用情况的预测后,我们会自动执行资源扩容。」

当预测的配额使用量超过租户配额的上阈值 (0.85 倍) 或低于下阈值(0.65 倍),就会触发扩容或缩容。扩容后,如果 Partition 配额超过上限  UP,还会触发 Partition 分裂;缩容后,我们会保证 Partition 配额不会低于下限 LOWER,以适应租户偶尔爆发的流量。

双层缓存机制 (挑战 3)

为了应对挑战 3(热 key 问题),一个朴素的想法是增加缓存机制。在 DataNode 实现缓存机制能够缓解 Hot key 问题。我们为 ABase DataNode 专门设计了一种 Size-Aware LRU strategy(SA-LRU),将请求按照 Value size 分为了多种请求,当请求插入 LRU 中时会同时插入到最底层和其 Size 对应层的 LRU 链表中。当需要淘汰请求时,我们会优先考虑淘汰掉 Size 更大的请求。在这种机制下,SA-LRU 不仅优化了资源利用率,还提高了总体缓存命中率。

然而,由于 DataNode 节点个数的限制,处理热 key 的极限能力有限。 所以我们通过 Proxy 支持缓存功能来对极热 key 承载能力做补充。但是传统的缓存方法 (如 LRU) 可能导致 Proxy 缓存的数据和 DataNode 上不一致,除此之外,客户端的随机路由算法会造成命中率的较低。

因此,我们和字节搜索架构团队同学 (论文作者 Qingshuo Li 同学) 进行合作。在 Proxy 端提出了两种优化策略:Proxy 缓存模块和客户端路由策略。

Proxy 缓存模块实现了 AU-LRU 缓存机制。AU-LRU 总的来说是一种带 TTL 的 LRU,但是会通过主动更新机制来解决由于缓存条目过期而导致的缓存穿透,会在 hot key 接近到期时自动刷新,从而保持缓存数据的及时性和连续性。

我们提出了 「limited fan-out hash strategy」 来路由请求。首先租户的 N 个 Proxy 被分为 n 组,每条请求都会使用哈希函数哈希到这 n 个代理组中,然后从这个 Proxy 组中随机选择一个 Proxy 来发送请求。那么我们就可以通过调整 n 来适应当前的 key 分布。假设当前用户的请求是 hot key 较多,为了避免多数请求都只被发送到少数 Proxy,那么我们可以把 n 值调小,这样 hot key 请求就能在一个较大的 Proxy 组中随机选择 Proxy 发送请求。另一方面,假设当前用户的 key 分布广泛,那么我们可以将 n 调大,这样每个 Proxy 收到的请求就只有整个 Hash 范围内的 1/n,有助于提高缓存命中率。

多租户负载均衡 (挑战 4)

为了应对挑战 4,ABase 集成了一个资源调度模块,由两个部分组成,分别是池内调度和池间调度。

池内调度算法主要由两个阶段组成。

  • 第一阶段的目标是平衡每个租户的副本分布,让副本尽可能均匀地在 DataNode 上分布;
  • 第二阶段的目标是平衡资源池内所有 DataNode 的资源利用率,从两个两个资源维度 (RU 和 Disk) 进行平衡,并且不会违反第一阶段建立的副本平衡。

两个阶段都使用了类似的算法,为了简洁起见,接下来我们重点介绍第二阶段。

Image

【图 5:负载均衡目标】

如图所示,第二阶段的目标是让不同负载类型租户分布在资源池内的不同 DataNode 中,以达成更高的资源利用率。

Image

【图 6:DataNode分类图】

我们会根据 Disk 和 Throughout 两种维度的负载占用率将 DataNode 分为三种不同的状态 SL、SM、SH,通过将 SH中的副本迁移到 SL中来实现负载均衡。我们会遍历 SH中的副本,计算出迁移每个副本的收益,选择收益最高的副本进行迁移。如图 7 所示,我们认为 DataNode 越靠近坐标系中心(最佳负载使用率),收益越高。经过负载再均衡后,理想状态是所有 DataNode 的两个维度的负载均靠近最佳负载率。

Image

【图 7:DataNode负载坐标系】

图 8 展示我们在实际生产环境中使用负载均衡策略的结果。可以看到,所有 DataNode 的负载点均向最佳负载点靠近,有效地缓解了资源倾斜度,促进了更好的资源利用并降低了与高负载数据节点相关的风险。

Image

【图 8:负载均衡结果图】

在池间重调度算法方面,它主要关注于在资源池之间重新分配 DataNode,该算法可以很容易地从池内算法扩展。例如,为了平衡两个资源池 PoolH(负载较高) 和 PoolL(负载较低) 之间的资源利用率,我们倾向于从 PoolL腾出一部分 DataNode 并将它们重新分配给 PoolH。首先,我们从 PoolL中选择一些低利用率的 DataNode,并将副本从这些选定的 DataNode 迁移到同一池 ( PoolL) 中的其他 DataNode。然后,我们将这些腾出的 DataNode 重新分配给 PoolH。最后,我们调用池内算法来重新平衡两个资源池内的负载。

经验教训

业务类型越丰富、资源池规模越大,资源利用率优化潜力越高。但是我们建议严格限定单个资源池容纳的「最大租户数量及资源池规模上限」。ABase 从事故中学习到的深刻教训是:保持适度的资源池数量和租户规模至关重要。当预期外的故障发生时,这能避免故障影响范围过大。鉴于资源池总配额应显著超过单个租户需求,我们相应设定了各租户的配额上限。

为了保持弹性,我们动态调节资源池规模,确保其闲置资源始终超过任一租户的配额上限。实际操作中,资源池总容量至少维持为单租户配额的十倍以上,且保证至少 20% 的资源处于闲置状态。这种设计既能为所有租户提供充足的弹性扩容空间,又能将闲置资源控制在合理比例。为保障租户秒级弹性扩容能力,我们不仅在资源池全局层面预留闲置资源,更在单机层级保持显著余量。每个层级的闲置资源都远超单租户配额,使得任意租户短期内均可快速实现配额翻倍,从容应对流量突增。

在多租户性能隔离中,我们遵循,对于可能触达共享资源限制的请求,「必须在这之前」有对单租户资源使用做限制。对于同一节点上无法实现的限制,通过前置组件来完成。例如 proxy 模块上对单分片流量做限制,以保证到达 DataNode 的单租户流量不超过 DataNode 拒绝能力的上限,然后再由 DataNode 对单租户流量 / 并发做限制,然后再由 DataNode 对整体流量 / 并发做限制。这样可以有效避免单租户占用共享资源过多而影响其他租户。

未来工作

「AI 评估租户物理资源」

当前 ABase 虽然基于 RU 和空间进行了多维度的负载均衡,但是在实践过程中,仍然存在 RU 不能充分表示租户 CPU/Mem/IO 实际消耗的情况。多租户场景下,获取指定租户的准确硬件资源使用很困难。RedisFamily 团队和 ByteBrain 团队持续合作。通过机器学习的方法训练模型来预测一个租户的 CPU/Mem/IO 维度的资源消耗,更加精细化的做到资源池内 / 资源池之间的各个资源维度的负载均衡。

「KVCache 场景的混部优化」

对于大模型推理加速的 KVCache 缓存场景,用户的吞吐高,Value 大。为了降低网络带宽消耗,ABase 提供直连模式。数据从 DataNode 直接返回给用户。对于特大 Value 场景,ABase 需要考虑对普通用户的请求影响。同时用户流量可能在多个模型间切换,这些需求对 ABase 的多租户流量管理提出了更高的挑战。

团队介绍

「RedisFamily 团队介绍」

RedisFamily 是字节跳动的 KV / 缓存团队,开发和维护字节的 Redis、ABase、PrisDB 等产品,致力于打造全球化部署的超大规模分布式 KV / 缓存数据库。为字节跳动集团业务和火山公有云业务提供统一的 KV / 缓存数据库解决方案。服务的业务包括抖音、TikTok、头条、小说、飞书、火山引擎、豆包等。RedisFamily 团队正在招聘相关方向的研发工程师和实习生,联系方式:[email protected]

「ByteBrain 团队介绍」

ByteBrain 是字节跳动 AI for Infra / AI for System 服务平台,旨在利用 AI 技术 (机器学习、大模型、运筹优化等),对基础架构和系统的全生命周期进行自动优化。ByteBrain 优化对象包括:数据库、存储、大数据系统、虚机、容器、网络,主要方向为 AIOPS、AI4DB、运筹优化、LLM4Infra,功能模块包括容量预测、资源调度、慢 SQL 优化、Text2SQL、数据冷热识别、参数优化、异常检测、根因分析、LLM-AGENT、智能问答等等。ByteBrain 团队正在招聘相关方向的研究员和实习生,联系方式:[email protected]