百度沧海·存储统一技术底座架构演进

本文整理自 2024 希捷数据+峰会的同名演讲。

随着 AI 时代的快速发展,对存储技术提出了更高的要求,尤其是在大规模、高性能和低成本方面。为了应对这些挑战,百度沧海·存储打造了一个高度可复用的统一技术底座。我们在这个统一的技术底座中解决了云存储的共性问题,让上层存储系统的迭代更高效。

首先,我将简要介绍一下百度沧海·存储统一技术底座的整体架构。这个统一的技术底座由三个核心组件构成,分别是统一的元数据底座、统一的层级 Namespace 以及统一的数据底座。

我们认为各种存储系统实际上是由元数据面和数据面两部分组成,通过提炼出高度可复用的元数据面和数据面的统一技术底座,就能积木式搭建各种云存储系统,比如对象存储、文件存储、块存储等,最大化减少重复开发的工作。

接下来,我将详细介绍这三个核心组件。

首先是统一的元数据底座。这个元数据底座是专门为元数据场景设计的分布式事务 K-V 存储系统。基于 Meta-Aware 的设计理念具备万亿级别的元数据存储能力。它支撑了对象存储 BOS 和文件存储 CFS/AFS 的元数据存储。

然后是统一的层级 Namespace。基于统一的元数据底座构建的层级 Namespace,已经进化到了单机分布式一体化架构,具有高性能和良好的可扩展性。

最后是统一的数据底座。这是一个在线纠删码 EC 存储系统,旨在提供高吞吐量和低成本的统一数据存储服务。该系统采用无逻辑单点的微服务架构,支持 ZB 级别数据规模

这三个核心组件共同构建了百度沧海的统一技术底座,支撑了上层的各类存储产品,如对象存储 BOS、块存储 CDS 以及文件存储 CFS/AFS 等。

目前,最新的第三代统一技术底座已经支持了百度沧海·存储数据湖加速方案 2.0 的规模化应用。

1.    统一元数据底座演进

首先介绍百度沧海·存储元数据底座的发展历程。这张图展示了我们三代架构的演进过程。

首先,第一代架构。在初期,我们的元数据存储分布在多套系统之上,比如对象存储,同时依赖于 MySQL 和一套分布式 K-V 键值系统。这种多系统并存的方式虽然满足了当时的需求,但也带来了高昂的运维成本。更为关键的是,MySQL 无法做到线性扩展,难以应对快速增长的数据需求。

随后,第二代架构,诞生于 2017 年。当时,HopsFS 论文的发表让我们看到了基于分布式事务数据库解决对象和文件元数据存储扩展性问题的可能性。受到启发,我们启动了自研通用 NewSQL 项目。经过两年的努力,文件存储 CFS 和对象存储 BOS 的系统扩展性得到了显著性提升。然而,尽管扩展性得到了改善,元数据的性能仍未达到理想状态,这成为我们下一步优化的重点。

进入第三代架构,自 2019 年起,我们意识到通用 NewSQL 的设计无法在云存储元数据场景中充分发挥性能优势。于是,我们深入分析了百度内部各个云存储场景的元数据特征,面向这些场景进行了重新设计,终于解决了上一代架构扩展性和性能难以兼顾的问题。当前这一系统已成为百度沧海·存储的核心支撑,完成了大规模全面的上线,服务于百度智能云的对象存储 BOS、文件存储 CFS 以及百度内部的类 HDFS 文件系统 AFS,极大地提升了产品的竞争力。

接下来我将讲述为什么通用 NewSQL 在元数据存储中会引入额外的开销。

主要原因在于通用 NewSQL 并不感知元数据的语义,这导致了多方面的资源浪费。为了更好地理解这一点,我将从 4 个维度进行详细阐述:分区(Partition)、事务与索引、单机引擎以及接口设计。

首先,从 Partition 的角度来看,元数据具有高度的局部性。例如,一个目录下的所有元数据,或者一个小规模文件系统的所有元数据,往往需要集中存储以提高访问效率。然而,通用 NewSQL 难以保证这种局部性,无法将相关的元数据全部放置在同一个 Shard 中。这意味着,当我们访问这些元数据时,常常需要跨 Shard 进行操作,从而导致跨 Shard 事务的高额开销,影响整体性能。

其次,谈到事务与索引,通用系统的事务处理往往伴随着较大的开销。以多版本并发控制(MVCC)为例,虽然它能够提高并发性,但也带来了额外的垃圾回收(GC)开销。此外,二级索引在通用系统中通常依赖分布式事务来保障原子性,这进一步加剧了系统的负担。分布式事务本身开销巨大,导致整体性能难以达到理想状态。

第三,从单机引擎的角度分析,通用 NewSQL 通常采用单一的单机引擎,目前较多采用 Log-Structured Merge-Tree(LSM-Tree)结构。虽然 LSM-Tree 在写性能方面表现出色,但在某些场景下并不理想。例如,对于数据量较小且主要进行点查询的表,LSM-Tree 的性能不如全内存的哈希引擎。这种不适配性导致了资源的低效利用和性能瓶颈。

最后,关于接口设计,基于通用 NewSQL 的实现,会导致元数据的语义与元数据的存储层彻底分离。这种分层解耦的架构虽然在软件工程角度有低耦合、高内聚的优点,但也带来了额外的开销。为了降低这些开销,我们需要将元数据的语义下沉到底层的事务 K-V 系统中,使得存储层能够更好地理解和优化元数据的操作,从而提升整体性能。

综上所述,通用 NewSQL 由于无法感知和优化元数据的特定语义,导致在分区管理、事务处理、存储引擎选择以及接口设计等多个方面产生了额外的开销。因此,我们需要针对元数据的特性,设计专用的存储解决方案,以更好地满足性能和扩展性的需求。

基于我们前面讨论的挑战,百度沧海·存储自主研发了统一元数据底座。

从系统架构上看,百度沧海的底座与业界的 NewSQL 系统相似,但在设计上有一个关键性的核心差异—— Meta-Aware。简单来说,这意味着底层的事务 K-V 系统能够深度感知元数据的语义,从而实现更高效的处理。

接下来,我将从 4 个方面详细阐述这一核心差异及其带来的优势。

首先,从分区(Partition)角度来看,支持自定义分裂策略和 co-located 机制。具体来说,我们能够确保一个目录下的所有元数据或一个文件系统中的所有元数据被分配到同一个 Shard。这一设计确保了大部分操作只需在单个 Shard 内完成,从而避免了跨 Shard 事务带来的高额开销,显著提升了系统的性能和响应速度。

其次,在事务与索引方面,元数据操作通常是短事务。我们实现了一个 TTL 为 5 秒的内存 MVCC 机制,只在内存中维持多版本,仅将一个版本进行持久化存储,这样就大大减少了多版本 GC 的开销。此外,我们同时支持了同步和异步二级索引机制,对于有强一致性需求的场景采用同步索引,对于那些一致性要求不高的场景采用异步索引,有效避免了分布式事务带来的高额开销。这种设计使得系统能够灵活应对不同一致性需求的业务场景。

第三,从单机引擎角度,统一元数据底座支持根据表的访问特征来选择最合适的存储引擎。目前,我们支持 LSM-Tree 引擎、全内存哈希引擎等多种引擎。例如,对于数据量大且有范围查询需求的场景,我们采用 LSM-Tree 引擎。而对于访问频繁且数据量小的表,全内存哈希引擎则能提供更高的查询效率。这种灵活的引擎选择确保了不同类型的元数据操作都能获得最佳的性能表现。

最后,在接口(SDK)设计方面,我们引入了协处理器机制,将文件存储的目录树逻辑下推到底层事务 K-V 系统,从而避免额外的 RPC 开销,加速了元数据操作的效率。

综上所述,百度沧海的统一元数据底座通过 Meta-Aware 的设计理念,在分区管理、事务处理、存储引擎选择以及接口设计等多个方面实现了显著优化,不仅有效降低了系统开销,提高了性能,还增强了系统的扩展性和灵活性。正是这些创新性的设计,使得百度沧海的统一元数据底座能够更好地满足我们复杂多变的业务需求。

2.    统一层级 Namespace 底座演进

接下来我将为大家介绍百度沧海的统一层级 Namespace 架构的演进历程。我们的层级目录树架构经历了三个阶段演进:

首先,第一阶段:类似 HDFS 的单机方案。在这一阶段,我们采用了类似 HDFS 的单机架构。这种方案的最大优点是极低的延迟,能够在高并发访问下保持高效的响应速度。然而,这种单机方案存在明显的扩展性瓶颈,其规模只能支持到 10 亿级别的元数据。随着业务规模的不断扩大,这一限制逐渐显现,无法满足未来更大规模的数据需求。

随后,进入第二阶段:基于分布式数据库的分布式 Namespace 方案。为了突破单机方案的限制,我们转向了基于分布式数据库构建的分布式 Namespace 架构。这一方案的主要优势在于可线性扩展,能够随着业务增长灵活地扩展系统容量。然而,分布式方案的本质是牺牲局部性来保障扩展性,而局部性的牺牲必然会带来性能的损耗,为了解决局部性牺牲而带来的性能瓶颈,百度沧海在第二阶段内演进了三代架构,不断优化分布式 Namespace。

最后,第三阶段:单机分布式一体化方案。我们提出并实现了单机分布式一体化方案,做到了规模自适应。当系统规模较小时,这一方案能够发挥单机 Namespace 系统的性能优势,实现百微秒级的低延迟。随着业务规模的扩大,系统能够无感平滑迁移到分布式架构,实现水平扩展,满足不同规模阶段的需求。这种一体化方案不仅兼顾了性能与扩展性,还大幅提升了系统的灵活性和可维护性。

在分布式层级 Namespace 优化上,我们主要解决了两个核心问题:

  • 第一个是单分区事务优化:通过将目录的属性信息分离,成为目录的孩子节点,同时,底层事务 K-V 控制属于同一个目录的元数据在同一个分片,我们将绝大部分的元数据操作从 2PC 优化到 1PC。
  • 第二个是路径解析优化:通过在传统表结构(Inode 表)之上引入目录索引表(Index 表)来加速路径解析。具体来说,就是把同一个文件系统的目录索引表放置在同一个分片中,即 Index 分片,实现了将路径解析从 N 次 RPC 优化到 1 次 RPC 调用。

除了加速路径解析,Index 的引入还带来了其他优化的机遇:

  • 加速目录 rename:Index 分片的引入除了加速路径解析,也为加速目录 rename 提供了机会,有了 Index 这个目录分片,我们就可以把目录 rename 执行过程中触发的分布式成环检测优化为单机成环检测,从而极大地提升目录 rename 的性能。
  • 加速写操作:由于 Namespace 之间是互相隔离的,一个 Namesapce 对应一个 Index 分片,这样我们就可以把时钟服务 offload 到 Index 分片,从而减少从 TSO 获取时钟这一次 RPC 开销。

然而,这个方案带来了以下技术挑战:首先,单服务器的 Index 分片面临单点性能瓶颈问题:

  • 读取性能瓶颈:尽管路径解析变成了单节点操作,但因为路径深度大部分超过 10 层,底层单机引擎的多次查询仍然会消耗大量 CPU 资源。
  • 写入性能瓶颈:所有的目录修改操作都需要访问 Index 分片 ,所以我们需要尽可能的提升 Index 分片的写入吞吐。

其次,快速路径解析可能导致同目录下的目录修改操作事务冲突和高延迟:

  • 事务冲突:当执行 mkdir 或者 rmdir 操作时,我们需要同时修改 Index 分片和 Inode 分片,这使得 mkdir 或者 rmdir 成为一个跨分片的两阶段提交(2PC)事务。如果并发向某一个目录下插入子目录,由于父目录频繁更新,会导致大量的分布式事务冲突。
  • 高延迟:目录修改操作触发两阶段提交协议,这会导致 mkdir/ rmdir 的高延迟。

百度沧海·存储团队通过一系列技术创新系统性地解决了上述问题。

单机和分布式架构能够做到合二为一的最核心的一个点是在规模达到临界点的时候,后端架构做到平滑切换。

我们具体的实现方法是这样做的:无论是单机架构和分布式架构,都基于我们自研的统一元数据底座去构建:

  • 在单机架构下,我们强制层级 Namespace 依赖的 Inode 信息和目录树信息绑定分配到同一组存储节点。这其实就是前面提到的 co-located 功能。这个时候,不需要跨机事务和多次 RPC 就可以完成文件创建、目录 rename 等元数据操作,这时候系统跟单机架构的延迟一致。
  • 当文件规模达到 10 亿量级的临界点之后,会触发分布式数据库按不同的表边界分裂。分布式数据库的分裂操作对上层业务无感,Inode 表动态水平扩展,这个时候单机事务转换为跨节点事务,单次 RPC 转换为多次 RPC。这样单机架构就可以平滑地过渡到分布式架构了。虽然,分布式架构的性能相对于单机架构有一些衰减,单次操作到毫秒级延迟,但是可以做到线性扩展。
  • 在单机架构下还有一个问题待解决,就是如何提升系统的吞吐。我们的做法是把文件语义操作下推到元数据底座上,减少跟上层组件的通讯次数,能够支持到几十万 TPS。

3.    统一数据底座演进

百度沧海·存储数据面的架构可以概括为三个阶段。第一个阶段,硬件上 HDD 为主、SSD 量较少,架构上采用的 master-slave 结构。一个管控节点管理数据节点,使用 3 副本系统的设计,单个系统只支持单个数据中心。

第二阶段,HDD 和 SSD 混用,SSD 量较第一个阶段上升,节点结构仍然采用 master-slave 结构,数据存储使用离线 EC,单个系统支持多个数据中心。

第三阶段,大规模使用 SSD,同时为了降低成本,开始使用磁带存储,并使用 PMEM 等加速硬件,采用无逻辑单点的微服务结构,数据存储使用在线 EC。

第一代架构的痛点显而易见,3 副本导致高存储成本,此外无法应对机房级别故障。尽管第二代架构在支持 EC 和多数据中心架构上有所改进,但依然存在以下显著问题:

  • 扩展性受限与高可用性不足
    • 架构瓶颈: 采用 master-slave 架构,扩展性受限于单机的存储空间和处理能力。
    • 单点故障风险:Master 作为单点,故障可能导致整个系统不可用。尽管使用一致性协议或者共享存储实现了高可用,但是这种方式的实现都是状态机方式,触发 bug 往往就会导致所有 master 节点故障。
    • 迭代效率低以 HDFS namenode 为例,其模块迭代需极为谨慎,任何问题都可能造成集群不可用,降低了系统的迭代速度。
  • 离线 EC 导致性能瓶颈
    • 写入过程复杂:数据先以 3 副本方式写入分布式 K-V 存储,当分布式 K-V 中单个分片数据写入达到阀值的时候,再通过读取进行 EC 编码,最终写入 EC 系统中。这样多次的读写操作消耗大量 CPU 和 I/O,导致写性能和读吞吐量降低。
  • 存放副本机制成本高
    • 固定副本数:采用 1.5 副本的 EC 编码系统无法进一步降低成本。

综上所述,由于第二代系统在 master-slave 架构带来的扩展性和性能瓶颈、离线 EC 导致的性能问题以及固定副本机制导致的高成本,成为制约系统进一步优化和扩展的主要障碍,第三代系统致力于系统性的解决上述问题。

百度沧海·存储最新的第三代数据底座架构采用了无逻辑单点的微服务架构。这里有两个关键词:一个是无逻辑单点,一个是微服务架构。

无逻辑单点的意思是系统中任何一组状态机停止工作,系统可用性和性能不受影响。微服务架构的意思是从功能的维度将原来单体的结构分拆,比如数据校验、数据修复、负载均衡、容量均衡等等,从而加快迭代效率。这种结构的好处是:可用性更高,扩展性更强,迭代效率更快,能够支持 ZB 级别数据。

同时,这个数据底座提供了高效 EC 机制,这个机制有两个特点:

  • 在线 EC:数据被 Put 进来后,数据面底座直接将数据进行 EC,然后将 EC 之后的各个分片 Shard 写入到对应的各个 DataNode 中去。在线 EC 的好处是不需要一个积攒和转储的过程,简化工程复杂度,减少转储之前的额外空间占用和 I/O 开销。
  • 可变 EC:提供 1.5、1.33 甚至更低的副本数。

在云存储系统的构建中,业界一般存在 2 种架构路线:

  • 分层架构路线:构建统一的分布式文件系统作为底座,进而基于该系统开发上层的云存储产品,包括对象存储、块存储、文件存储等。
  • 组件化架构路线:通过提炼出统一的元数据和数据面组件作为底座,上层的云存储产品基于这个组件式底座进行积木式进行搭建,比如百度沧海·存储的架构路线。

目前,这 2 种路线已在不同云厂商落地,均打造出了优秀的云存储产品,很好地助力了社会经济的发展。关于百度沧海·存储统一技术底座架构的更多技术分享,围绕扩展性、稳定性、高性能等方面的讨论,将陆续在「微信公众号——百度智能云技术站发布,欢迎大家持续关注。