数据湖系列之四 | 数据湖存储加速方案的发展和对比分析
本文按照数据湖存储加速方案的不同发展阶段铺开,比较了各类方案之间的异同,并深度剖析了这类方案的技术本质。
我们期望本文能够帮助读者对大数据和 AI 场景下的「数据湖存储加速」这个主题建立一个整体把握,为选出适合自己业务的方案提供参考。
24 年初,我们和客户 H 进行了交流。当 23 年大家都在训练自己的大模型,H 客户扩大了已有的 GPU 集群规模,加上既有自建 IT 基础设施,开启了大模型训练之路。在大模型加持下,新的业务效果很快得到了证明。
随着时间推移,大模型业务的不断扩大,基础设施层面碰到了一些跟存储相关的问题:
数据规模:要进一步提升模型效果,就要把更多数据喂给 GPU,但自建的小型文件系统已不足以承载这么多训练数据。曾尝试过 HDFS,虽然容量规模增大不少,但元数据量仍然存在上限,因此不得不将海量小文件打包存储,训练前再解压展开,训练后还得清理,使原本顺畅的业务流变得复杂。
存储成本:随着多模态的引入,业务数据由几十 TB、数百 TB 快速积累到数 PB,存储成本越来越不容忽视。
训练速度:算力规模逐步扩大,无论自建文件系统还是 HDFS,都开始跟不上算力需求,存储成为拖慢训练的主要因素。
类似客户 H 遇到的这些问题的例子还有不少。他们中大多都经历了从自建 IT 基础设施到开源大数据生态的时期,并尝试将以前的经验复制到 AI 场景。
的确,过去由数据库、数仓、ETL 等技术驱动的商业智能成为业务的强大助推器,但这种围绕预定义 schema 层层裁剪模式所设计的存算架构在 AI 面前显露出不少弊端,尤其是受系统扩展性和成本的制约,大量原始数据不得不被舍弃。
但是,数据正是大模型时代的黄金和石油,当业务希望从这些宝贵的原始数据中重新构建智能、提炼新的价值时,往往发现为时已晚。
1 数据湖存储成为云原生时代的事实标准
对于这位 H 客户,我们给的建议是拥抱云原生数据湖。其中最核心的主张就是将各类原始数据统一入湖,集中存储到同一数据底座,再以开放统一的接口提供给各类上层计算和应用。这种方式最大限度保留了数据的 Single Source of Truth,同时也解决了这位客户的困扰:
近乎无限的扩展能力:越来越多的数据湖存储已由传统的 HCFS 架构走向了对象存储架构,其平坦元数据结构天然适合水平扩展,单个存储桶轻松承载千亿对象,尤其在 AI 这类海量小文件场景具有得天独厚的优势。
灵活的资源弹性:相对于 HCFS 的存算一体架构,云服务商提供的对象存储通常基于存算分离的庞大资源池,客户按量付费、按需扩缩容,同时还能借助资源池的规模效应满足一定的突发性能需求。
极致的存储成本:对象存储一般采用纠删码技术,相对多副本可带来数倍的空间节省,同时从标准、低频、冷存到归档的分级存储能力,也给原始数据的长期保存提供了进一步优化成本的方案。
当然,这些优势不仅局限于 AI 场景,在大数据场景下同样能发挥很大的价值。除了比 HCFS 拥有更好的扩展性、资源弹性和成本优势外,类似 Hudi、Iceberg 等新一代存储格式和计算范式也在围绕对象存储的这些特性进行设计优化。可以看到,基于对象存储的数据湖已成为云原生时代的事实标准。
2 为什么还需要给数据湖存储加速?
回到 H 客户的例子。虽然对象存储解决了他的海量数据规模和存储成本的问题,但存储拖慢训练的问题仍然没有解决,甚至在某些情况下可能更差!要弄清楚原因,我们仍以 AI 训练为例展开分析。
如图展示了一个典型的 AI 训练过程。每一轮训练首先需要对原始数据进行遍历和打散,然后以多个 batch 喂给 GPU 完成训练迭代,多次迭代间还会保存 checkpoint 用于中断恢复。
我们注意到大多数训练尤其是视觉、多模态训练往往依赖大量小文件作为输入。因此除读写 checkpoint 外,训练与存储的交互主要集中在两个方面:一是大目录下海量文件的遍历,对应对象存储的 LIST 操作;二是小文件的高频重复读,对应 HEAD 和 READ 操作。
再从对象存储侧来看。虽然其平坦目录结构、纠删码编码方式和存算分离架构解决了扩展性、成本和弹性问题,但也导致 LIST 和对小文件的 HEAD、READ 性能可能比不上传统的 HCFS 架构:
元数据性能:平坦目录下 LIST 操作需要扫描整个子树并折叠不需要的深层子项,导致训练数据的遍历耗时较长。
小 I/O 性能:采用 HTTP 协议,每个 LIST、HEAD、READ 操作都需经过 LoadBalancer、WebService 等很长的链路才能到达底层的元数据和数据集群,且纠删码还可能导致小文件读放大。这些因素叠加造成训练数据小 I/O 延时并不理想,很可能会拖 GPU 的后腿。
带宽限速:存算分离架构下,计算往往位于 overlay 虚拟网络,访问对象存储需先穿透到 underlay 网络,且计算与存储间可能还存在跨机房的物理网络距离。因此大量重复读不仅产生可观的带宽成本,而且很容易触发各环节的限速,进一步制约训练效率。
类似地,对大数据场景进行分析也会看到同样的问题。我们将 AI 和大数据各类典型场景总结如下,发现部分场景依靠对象存储自身能力就能很好地满足,但另一些场景则需要额外的存储加速,才能保证计算效率,减少算力和带宽资源的浪费。
3 数据湖存储加速的诞生和发展
早在数据湖被提出之前,随着高性能计算(HPC)需求的产生,存储性能的提升就已经得到了广泛关注。这一阶段,HPC 等应用开始由 NAS 类容量型存储转向以并行文件系统为代表的高性能存储。
3.1 并行文件系统
并行文件系统,代表产品如 GPFS、Lustre、BeeGFS,最初面向复杂多媒体处理、气象分析、工程计算、生命科学等超算场景设计。通过客户端到后端数据节点的直连、数据条带化、MPI-I/O 等机制实现并行读写,从而在当时主流的 HDD 上也能提供出众的存储性能。后来基于 SSD 实现了更极致的性能,被广泛应用于 HPC 和 AI 场景,成为很长时间内性能场景下近乎唯一的选择。
不难想象,假如经费无限,将所有数据全部放入并行文件系统,几乎就能满足应用对高性能存储的所有诉求。但实际上对于数据密集型业务来说,完全基于一套大容量的并行文件系统,存储成本势必成为不容忽视的问题。
面对巨大的成本问题,研发工程师们会提出什么样的方案来解决呢?
3.2 兼顾成本:并行文件系统 + 对象存储
在 HPC 和 AI 场景,近年来开始将并行文件系统与对象这类低成本存储组合使用。在这套组合中,两者并不是对等的,而是处于上下两层。根据数据入口和存储中心所属层级的变迁,可细分为两个阶段。
第一阶段,数据入口和存储中心仍然在并行文件系统,对象存储只作为其过期数据沉降或备份的冷存储层。
第二阶段,随着数据湖进入大家的视野,数据入口和存储中心开始下移至对象存储底座,而计算所需的热数据向上导入并行文件系统。这种形态下,我们已经可以把并行文件系统视为对象存储的缓存加速层。
不过这种加速方案有两个问题需要改进:
其一,两者仍然相对独立,通过副本式的拷贝来建立数据的弱关联,任意一侧的数据变更无法透明地传递到另一侧。因此业务需要提前规划数据冷热,仔细控制两层间的数据交换。有研发能力的企业通常会在业务层额外建设一套专用的数据流转管理系统。
其二,正是由于这种不透明性,不能做到按需加载,因此需要把即将用到的数据全部载入并行文件系统。因此,业务所需的并行文件系统规模,只能由数据量和所需 I/O 能力两者的最大值来决定,很难做到各类场景下 I/O 和容量均不浪费。目前只能通过不断细分的产品规格来满足差异化需求。
面对不透明性问题,研发工程师又是如何解决的?
3.3 透明流转:对象存储 + 缓存系统
出于对上述两个问题的改进,大家开始思考更透明、性价比更高的方案。诞生于 UC Berkeley 的 Alluxio 就提供了其中一条演进思路。
Alluxio 最初的思想是在计算侧构建一层虚拟的分布式文件系统,从而对各类上层业务屏蔽底层存储差异。这种统一访问界面之下的数据编排能力,可实现底层异构存储间的数据流动,因此被大量用于跨系统、跨云的统一存储业务架构。实际上这跟 VFS 在 Linux 操作系统中的角色非常相似。
Alluxio 的另一个贡献则是促进了近计算分布式缓存的发展。这让大家意识到如果将计算节点空闲的内存、SSD 等资源统一托管起来,用作对象存储的透明缓存,不仅不增加成本,还能获得非常好的加速效果。相对于对象存储引擎内部的缓存机制,这类近计算缓存直接工作在业务 VPC 的 overlay 网络中,时延能降低一个数量级,同时与计算框架配合实现数据协同调度的发挥空间也更大。因此近年来,各大云服务商纷纷推出了自己的缓存加速产品,比如 AWS 的 FileCache、百度智能云的 RapidFS、阿里云的 JindoFS、腾讯云的 GooseFS 等,在 AI 和大数据的大部分场景下都能取得接近并行文件系统的加速效果。
Alluxio 这类真正的缓存系统用于对象存储加速,相对于采用并行文件系统的加速方案,最大的区别在于两层间的数据关联和双向流转完全透明化,从而能做到:
基于同一套存储底座,避免数据反复拷贝,最大程度发挥对象存储在打通业务上下游上的优势。
透明缓存降低了运维人员在业务规划、控制数据流转上的心智负担,内置流转能力就能满足 80% 以上的需求。
通过 pipeline 换入换出,更好地解耦了热数据性能和容量成本,I/O 和容量的配比可根据场景灵活定制。
当然,缓存系统也有其固有的问题,在使用中需要注意:
其一,虽然缓存的透明性能避免数据加载不完整引发的读失败,但是 cache miss 还是会导致明显的性能波动甚至降级。因此对这类产品的缓存算法、调度策略和数据流转效率提出了比较高的要求,这也是各产品持续深耕的重要能力。当然,不同场景对数据的需求总是不尽相同的,如果产品既提供了完善的内置策略,又能将自定义策略的能力开放出来,则能更加贴合业务需求,保证长尾数据的加速效果。
其二,缓存系统的写操作通常还是基于对象存储的原生能力,因此类似追加写、边写边读、子树 rename 等操作仍然会受对象存储的限制。一般来说,AI 场景这类需求较少,但大数据场景对此存在比较强的依赖。
为了解决对象存储在大数据场景写能力上不足的问题,研发工程师是如何设计新的解决方案的?
3.4 完备语义
事实上,上面提到的缓存系统写操作的局限性来自元数据和数据语义两个层面。元数据层面,对象存储平坦目录结构导致 rename 这类子树操作实际上需要对子树下所有对象依次操作,非原子、耗时长。而数据层面,底层存储引擎只提供一次写入能力,不支持流式追加写。针对这两个问题,业界提供了两类解法。
3.4.1 解法一:云原生文件系统 + 对象存储
这种方式是在对象存储之上重新构建一层近计算的文件系统,用来解决文件语义和性能加速两个问题。对象存储层只提供持久化数据底座,继续发挥其扩展性、弹性和成本优势。
以 JuiceFS 为代表,上层重新组织层级目录结构的文件元数据,并存储在 Redis、TiKV 这类外部元数据引擎中。而数据切块后写入对象存储,将以前对整个对象的更新缩小到对其中一个小数据块的更新,从而满足追加写、边写边读等语义需求,因此这种方案在大数据场景得到了较为广泛的应用。性能上,对于存在大量共享数据需要加速的高性能场景,可以使用其商业版提供的分布式缓存功能。
不过在实际应用中还需要考虑由此带来的数据侵入性问题。
由于文件切块后持久化,因此对象存储层丢失了路径、文件名中所包含的关键业务信息。这样就很难再完全利用对象存储的生态能力进行数据处理、数据管理、生命周期流转和成本优化。为了缓解这个问题,JuiceFS 最近的版本支持了存量对象导入和文件到对象的导出,可以实现一次性的单向拷贝。不过,当建设多套业务系统时,需要同时建设多个近计算文件系统实例,因此可能需要多次导入导出才能满足多套业务系统间数据交换的需求。
3.4.2 解法二:文件对象融合 + 缓存系统
为了原生支持完备语义,一些云服务商又探索出第二种解法,即直接在对象存储层内部实现元数据和数据两个层面的文件对象融合。缓存系统仍然在近计算侧提供性能加速。
元数据层面,构建可无限水平扩展的层级目录服务,向上同时提供两套接口,实现文件和对象两种数据视图的互融互通。
数据层面,在存储引擎内部支持流式追加写模型,消除对象存储写能力上的局限,从而更完整地满足大多数存储场景的需求。
采用这种文件对象融合存储作为数据底座和数据流动的主管道,叠加各业务环节的近计算缓存,既能满足完备语义、性能加速和透明流转的需求,又能避免对业务数据的侵入,获得更多对象存储的丰富功能和原生体验。
4 数据湖存储加速都在解决哪些关键问题?
上面我们对数据湖存储加速的诞生和发展进行了总结。虽然市面上各产品的思路和具体实现有差异,但在需要解决的关键问题上又是大致相同的,不外乎元数据加速、数据读写加速、数据流端到端提效三个方面。从这几个方面一探究竟,可以帮助我们更好地理解其原理并挑选出适合自身业务的存储加速方案。
4.1 元数据加速
存储系统的元数据是用来管理数据的数据,比如目录结构、文件名称、大小、修改时间等。业务发起读写操作前,都需要先与元数据交互,尤其在大数据 AD-HOC、AI 训练等涉及大量小文件或小 I/O 的场景中,元数据耗时的占比甚至接近数据读写本身。因此其性能好坏对存储的整体表现有很大影响。
大多数业务程序已习惯本地文件系统的用法和层级目录视图。而前面提到,对象存储用平坦目录来模拟层级目录的开销,以及较长的网络距离和请求处理路径都对元数据性能带来负向影响。因此几种加速方案都绕不开近计算原生层级目录树这一做法。
部署形态上:在业务 VPC 的 overlay 网络中部署元数据服务,能大大缩短访问路径。同时一般还会利用内存作为热点元数据缓存,从而将访问时延从十毫秒量级缩短到毫秒以内。
元数据语义上:并行文件系统、云原生文件系统内置了层级目录树,LIST、RENAME、DELETE 等操作都是以操作子树根结点的方式进行,避免了额外开销和非原子性带来的问题。缓存系统的层级目录树同样能对 LIST、HEAD 这类读操作进行加速,但更新操作通常采用写穿透的方式来保证与对象存储的一致视图。因此对于大数据中某些更新操作较多的场景,一般会选择在对象存储中也采用层级目录模式的桶,以保证穿透写操作的效率。
元数据规模上:层级目录结构的性能与扩展性往往相互制约,很难同时将两者做到极致。几种加速方案都是在两者间权衡的结果,具体可分为横向扩展和垂直分层两种思路:
加速层内横向扩展:并行文件系统是按一定规则将全量元数据打散到多个数据节点来解决扩展问题,是完全去中心化的设计。缓存系统一般也支持类似子树划分的方式在多组元数据服务间扩展。但当集群规模很大、更新操作较多时,两种做法都可能因节点间通信的增多而影响性能。云原生文件系统则取决于采用哪种元数据引擎,如果采用 Redis 这类引擎则规模上限通常只有一亿左右,如果采用分布式 KV 引擎则可做到与对象存储类似的扩展能力,但可能需要舍弃极致时延。
加速层与对象存储间垂直分级:常见于缓存类方案,即加速层只缓存最热的小部分元数据,较少访问的大部分仍然保持在对象存储层。这种做法整体扩展性接近对象存储,不过当元数据不命中时时延波动较大。如果业务对元数据的访问具有明显的局部性特征,则适合采用这种方案。
4.2 数据读写加速
数据读写是计算跟存储交互最多的部分。如果读写慢于计算则会导致任务等待和算力浪费。尤其当面对价格不菲的 GPU 算力时,这个问题愈发受到关注。
对于对象存储来说,影响读写性能的主要因素:一是存算分离架构导致的网络距离和带宽限速问题;二是 HDD 介质性能和存储引擎能力的限制;三是较长的请求处理路径对时延的影响。因此数据读写加速的思路也大致围绕这几个方面展开。
第一,近计算访问:在分析元数据加速时已经提到,加速层近计算部署可以明显缩短网络距离,降低读写时延,对于数据面来说更是如此。并且对同一份数据的多次重复读,可通过近计算缓存节省大量带宽,避免对象存储主动限速的影响。
第二,采用高规格硬件和优化的存储引擎:加速层通常采用 NVME SSD 存储介质、与之匹配的高性能单机引擎和 RDMA 高性能网络,相对于直接访问对象存储可带来数量级的时延降低。而在对象存储层内部,一些产品也通过原生支持流式存储引擎,相对过去的 Blob 引擎提供了更接近文件系统的读写表现。
第三,软件架构和 I/O 链路优化:有了近计算网络环境和高规格硬件,如何将它们充分利用起来,需要依靠加速层的软件架构设计和 I/O 链路优化。在这一点上各产品的做法不尽相同,但基本思路不外乎两点,提高扩展能力和缩短 I/O 路径。以读加速为例:
这里所说的扩展能力,指的是架构层面怎么将数据分布和读请求均匀打散、充分并发,从而最大限度榨干所有硬件。
并行文件系统、缓存系统一般会将完整文件细粒度切分为若干数据块或条带,再按一定规则打散到多个存储节点的多个盘。打散规则通常按哈希计算,因此能避免访问链路上出现单点瓶颈。云原生文件系统也是将切块后的数据写到对象存储,方便文件系统层以并发方式提高读写性能。
某些系统还支持多副本,客户端可根据实时负载动态选择合适的副本读取。对于譬如超大规模词典、模型分发等多实例启动风暴的场景而言,多副本能进一步将 I/O 均匀扩散到整个资源池,避免因局部热点导致的请求排队和性能抖动。
而缩短 I/O 路径,指的是怎么让数据尽可能被就近获取。
分布式层面,从对象存储,到加速层数据节点的 SSD 和内存,再到计算节点本地的客户端内存,数据会经历从最慢到最快的多级流动。在各级配置合适的预取、预读和缓存策略,让可能被多次访问的数据提前加载并驻留于更快一级,能够降低后续读取时延,减少带宽消耗和触发限速的可能性。
单机层面,过去为了实现简单,一般直接基于内核提供的原生 FUSE、PageCache 等机制来实现客户端读写逻辑。近年来的存储加速系统越来越多地深入到与内核交互的地方进行优化,例如借助 virtiofs、零拷贝、用户态缓存等机制大幅降低内核与用户态文件系统间的通信开销,本质上也可视为单机内部缩短 I/O 路径的做法。
当然,对于写加速也有类似的优化手段。例如缓存系统的单端写,可先写计算节点本地的内存和 SSD 即返回(缩短 I/O 路径),然后异步将这些数据按不同区段并行写入底层对象存储的大资源池(提高扩展能力),从而成倍提升端到端写吞吐。
4.3 端到端提效
有了上面介绍的元数据和数据读写加速,还有一个关键问题是在业务流中如何将这些能力串起来、利用好,最终实现端到端提效。事实上,在对实际业务的长期观察中发现,数据流转不畅往往成为降低业务效率的更重要因素。
我们可从三个层面来分析这一问题。
第一,业务如何低成本接入:对象存储通常提供 HTTP API 的访问接口,但不论从性能还是兼容性来看,这种接口对大数据和 AI 业务都不够友好。存储加速产品往往会提供更低成本的接入方式。例如对于大数据,提供业界广泛采用的 HCFS SDK 客户端,可与 Hadoop 生态无缝对接;对于 AI,则提供 POSIX 兼容的挂载客户端,使得基于本地盘和传统自建存储的数据科学家能将各类实验和生产任务无感迁移上来,大大降低了业务的适配成本。
第二,单个业务环节内如何高效数据流转:对于业务流中的某个具体节点,只有让数据在合适的时机出现在合适的位置,才能发挥好存储加速的作用。在这一点上,缓存系统通常能提供最为灵活的机制和策略,通过与上下层配合来优化数据调度和缓存效率。
向下与对象存储深度集成,建立双向数据关联。早期产品只提供了手动指定目录的数据加载和沉降方式,后来开始支持 Inventory 清单导入、周期性自动加载、增量同步、读时按需加载、自动淘汰等丰富功能,有的产品进一步将策略开放给业务定制,例如根据文件名后缀、大小、路径等规则实现更智能的数据流转。
向上与计算引擎和调度框架配合,通过 pipeline 的方式进行数据调度。例如在大数据场景下,哪张表、哪些列需要载入加速层,可由计算引擎发起精准的调度指令。在 AI 场景下,训练框架通过样本列表,通知加速层提前准备下一轮需要用到的数据。在数据集超过加速层容量的情况下,通过这种方式可实现多轮训练数据间的无感换入换出,从而利用有限资源实现透明的全量数据加速。
第三,多个业务环节间如何做到数据畅通:实际业务往往涉及上下游多个环节的配合。例如大数据 ETL 将一级输出作为下一级的输入,数据预处理的输出作为 AI 训练的输入,训练产出的模型作为推理的输入等。这些业务节点间的数据流动和共享就是贯穿其中的关键。
并行文件系统、云原生文件系统这两类方案以自身作为数据的访问入口,通过副本式拷贝来建立与对象存储数据的弱关联。当多个业务节点需要从不同地域、不同入口分别访问时,数据共享就不够方便。
缓存系统这类方案,与对象存储中的数据建立双向强关联,任何业务节点的写入都可透传到对象存储底座。一些缓存产品还借助对象存储的事件通知等机制让这些更新近实时可见,这在需要频繁交换数据的业务流中可带来近乎透明的统一存储底座使用体验。
最后,回到本文开篇提到的案例,客户 H 最终需要一个什么样的数据湖存储加速方案呢,除了技术因素之外,还有其他维度需要考虑吗?
版权声明: 本文为 InfoQ 作者【Baidu AICLOUD】的原创文章。
原文链接:【http://xie.infoq.cn/article/2e8ff60f54152bf3d6a76d283】。文章转载请联系作者。
评论