高性能、云原生湖仓一体存储架构探秘
一、湖仓一体存储架构的演进
1、存储架构的演进阶段
大数据存储系统的演进,分为两个阶段:机房时代和云计算时代。
第一个阶段,也是最早 Hadoop诞生的时代,这个时代主要以机房的系统为主, HDFS 基本上是唯一的存储选型方案。
随着云计算的普及和发展,对象存储逐渐成为企业主流的存储方案。尤其是在数据湖架构中,对象存储以其高度可扩展性和对多样化数据类型的支持,成为一种流行的底层存储解决方案。我们将回顾并对比HDFS和对象存储的架构,探讨它们各自的优劣势以及发展趋势。同时也会探讨云原生的数据湖存储架构应该如何设计。
如果从本质上去分析 HDFS 和和对象存储的架构设计,会发现其实是两个完全不一样的存储系统。未来在云原生时代里的存储系统,为大数据湖仓一体提供存储底座,应该具备哪些特征呢?后面我们会对此进行探讨。
2、HDFS 的特征
HDFS 起源于 GFS ( Google File system ) , 于2006年正式发布。它的特点包括独立的元数据存储(NameNode)、多副本、存算耦合等。整个 HDFS 设计,对于大文件存储相对来说更友好,对于小文件没有那么友好。在存储的规模上,一般单个命名空间(Namespace)在亿这个量级。如果有更大量级的数据存储,需要做一些扩展性的架构调整。
3、对象存储的特征
对象存储 S3 也是于2006年发布,但它发布之初的目标其实是存储海量的非结构化数据,并不是给大数据生态使用,所以对象存储的架构设计和 HDFS 完全不一样。它主打的特点,包括存储成本低、数据的持久性足够高等方面。它的 API 是基于 HTTP 协议,元数据设计是扁平的 KV 结构,扁平的元数据设计在大数据场景中会带来一些问题。和 HDFS 一样,它的数据也是不支持修改的。在一致性上,对象存储以最终一致性为主,部分的云厂或者部分接口可以实现强一致性。
二、不同类型存储系统比较
下面整体对比一下前面提到的 HDFS 和对象存储在不同维度的特性。
首先存储规模直接决定了数据平台或者整个大数据系统能够支撑的业务数据量,相比HDFS,对象存储能做到更大的存储规模。在日常的数据任务中,比如 ETL 或者ad-hoc,元数据操作性能会影响整体的任务效率,这一点对象存储的性能要远远弱于HDFS。运维复杂度也是影响存储选型的关键因素,HDFS的维护成本相比对象存储会高不少,这其中既包括人力成本,也包括为了保证高可用和扩展性额外搭建的一些组件等。
1、HDFS的阿喀琉斯之踵-NameNode
HDFS 最大的瓶颈是 NameNode。NameNode 最开始的设计是模仿 GFS 的 Master,这是一个单点设计,所以 HDFS 也有同样的问题。
在单点的 NameNode 扩展性问题上,社区做了一些尝试,比如 ViewFs + Federation 的方案,后面又推出了RBF(Router-based Federation)方案,本质上是为了解决单一命名空间下 NameNode的扩展性问题,通过多集群横向扩展的方式保证 HDFS 集群存储容量的规模不会受到大的限制。
除了存储规模以外,另外一个问题就是高可用问题。在单一集群里面 NameNode 是一个单点,所以后来有了 Standby 的 NameNode,再往后有了 JournalNode。归根结底,所有这些组件、架构都是为了保证整个 HDFS 存储集群能够满足高可用的需求,而不是一旦 Active NameNode 宕机之后整个大数据集群就不可用了。
2、对象存储的阿喀琉斯之踵-元数据
对象存储的问题也跟元数据相关。前面提到了对象存储的元数据设计是一个扁平的 KV 结构。上图中的foo目录有这么多子目录或者子文件,从对象存储角度来说,它并不是目录结构。我们在传统的文件系统里面看到的是树状的目录结构,但在对象存储的设计里面并不存在一个目录树,而是一维扁平的 KV 结构,再通过“/”分隔符来模拟目录树。
如果要在对象存储里面实现目录的重命名,例如将foo这个目录重命名为 bar 目录,对象存储的实现上会有这样一些步骤:第一步需要先递归地拷贝,如果是很深的目录,那需要去递归拷贝整个目录的数据到以 bar 为前缀的位置;第二步是更新内部的一些索引;最后把原始的 foo 目录下的相关数据都删掉。
整个流程虽然看起来只是一步重命名文件夹的操作,但在对象存储内部拆分成了几个大的步骤,每个步骤其实看起来都不是特别的轻量,特别是第一步和第三步。
我们自然会有一个疑问,在这个过程中,尤其是当要重命名一个很大数据量的目录时,一致性如何保证?其实对象存储是没有办法保证的。社区有很多的组件去尝试解决,或者基于对象存储的接口去优化。但也只能是尽量去规避一致性问题,并不能完全解决。因为对象存储架构设计上的问题是没有办法通过外层的组件简单绕过的。
3、对象存储元数据性能及API限制
在数据湖的组件里面,不管是 Hudi 还是 Iceberg,都提到过对象存储的一些问题,比如上面这个表格是 Hudi 文档里面提到的,在对象存储上如果要 List 一个目录,随着目录的文件数的增多,它的延迟会逐步地增长,所以 Hudi 社区才有了 Metadata Table 设计。
Iceberg 在文档里面也提到对象存储会有一些基于某一个固定前缀的 API QPS 限制,这样就会导致频繁访问的目录或者大的数据集如果触碰到这个 API QPS 限制,任务的稳定性会直接受到影响。特别是当你的数据平台的规模变得比较大以后,就很容易触碰到云上对象存储的 API QPS 限制。所以 Iceberg 有了 ObjectStorageLocationProvider,去尽量规避这个问题。它本质上是一种规避的实现方式,在不同的Key 前面加一个随机的哈希,这个哈希就是为了尽量把基于前缀的 QPS 限制分散到不同的前缀,从而避免很快触碰到 API QPS 限制。
三、探索湖仓一体架构未来的存储选型
假设现在从 0 开始去设计,基于当下云计算或者云原生的环境去构建一个更适合现在和未来的存储系统,它应该具备哪些能力呢?
1、技术关键点
这里列了对于大部分场景来说都比较重要的一些点:
首先最重要的就是扩展性的问题,随着大数据平台的架构从数仓、到数据湖、到湖仓一体不断演进,对于平台存储容量的要求会越来越高,不仅仅是满足以前传统数仓的存储需求,还要尽量多地去涵盖越来越多的业务,以前在不同的存储系统里面管理的数据,接下来可能需要在一个地方统一存储和管理,避免数据孤岛的问题。
第二个很重要的点是高可用问题。整个系统肯定要具备高可用的能力,不能只有扩展性没有高可用,否则也是无法满足生产需求的。
第三点是性能问题。不能为了存储大容量的数据,就以牺牲性能为代价,需要一定的优化策略保证性能,包括元数据的性能以及数据读写的性能。
接下来是尽量多地利用云的优势,云最大的特性就是弹性,计算和存储都可以做到弹性。如果整个存储系统架构不能够适配云的弹性伸缩特征,还沿用以前机房的存储架构,就不能最大化发挥云的特点。
云上天然是存算分离的场景,如何将整个生态里面的计算组件和存储组件分离,分离后还能保证整体的计算效率以及扩展性等,都是底层的存储系统需要考虑的问题。
小文件管理更多是数据湖需要考虑的问题,传统的大数据平台可能还是以大文件为主,但是小文件问题会随着 AI 或者其它领域的数据进来后越来越凸显。
针对以上每一个技术关键点都会有一些对应的解决思路,如上图所示。
2、JuiceFS
今天给大家介绍的 JuiceFS,定位是强一致性的分布式文件系统,可以认为这个设计目标是用来完整替代 HDFS 的。
JuiceFS的整体架构分为三块,跟 HDFS 很像,元数据的存储、数据的存储、以及不同客户端的接口。但其实每一块都会有一些不一样的设计。
首先元数据在 JuiceFS 里面是插件式的引擎设计,不同的场景可以选择不同的数据库,比如Redis、MySQL、TiKV等等,你可以根据业务场景、业务需求,挑选最合适的数据库来作为JuiceFS的元数据引擎。
数据存储方面, JuiceFS 主要基于对象存储,可以最大化利用云上的存储资源,所有数据都放到对象存储里,但并不是简单地写到对象存储里就完了,通过 JuiceFS 客户端写进来的数据,会做一些格式处理(类似HDFS DataNode的block设计),元数据会尽量不依赖对象存储的元数据,因为前面提到了对象存储的元数据是有很多问题的,如一致性问题、性能问题。JuiceFS不依赖对象存储的元数据,而是由自己独立的元数据存储来承担所有的元数据请求。
元数据引擎可以横向扩展,对于海量级(比如百亿级)的文件,不管是大文件还是小文件,都能够很轻松地存储。在JuiceFS用户的生产环境里也的确做到了百亿级规模,很好地证明了存储系统具有优秀的扩展性。
缓存是很重要的特性,特别是在存算分离的场景下。如果需要升级到存算分离的架构肯定要考虑缓存功能,不管是元数据的缓存,还是数据的缓存都是有必要的。
四、湖仓一体架构在JuiceFS上的实践
1、湖仓一体架构
这是整体架构图,最下面既包含传统的结构化数据,也有很多半结构化和非结构化数据。存储层是 JuiceFS 结合对象存储提供统一的存储底座。
再往上是 Delta Lake、 Hudi 或者 Iceberg 这样的数据管理层,它会基于底层的存储系统 JuiceFS 、 HDFS 或者对象存储之上,再对整个数仓进行管理。
然后是各种查询引擎、BI 工具、计算引擎接入不同的管理层组件。
2、元数据性能比较
上图中比较的是以 OSS 为代表的对象存储,以及 HDFS 和 JuiceFS 。左边是元数据的 Latency,这个值越低越好;右边是吞吐,值越大越好。
首先看蓝色的对象存储和另外两个相比,Latency 在不同的元数据上操作的差别会很大。特别是前面提到的 Rename操作,相比另外两个存储系统来说,在对象存储上有非常大的性能下降。HDFS 和 JuiceFS 在架构设计上是相似的,都有独立的元数据存储,所以不管是 Latency 还是吞吐上都能做到比较快的程度。右边这个图中 JuiceFS 相比 HDFS 在吞吐上甚至能做得更好。
3、数据查询性能比较
上图是数据查询性能比较。左边这个图是用 Spark 在对象存储和 JuiceFS 上做的 TPC-DS 查询测试。右边这个图是用 Presto 在 HDFS 和 JuiceFS 的测试。
左边图中可以看到,在查询性能上, JuiceFS 相比直接使用对象存储能有成倍的性能提升,特别是在一些相对比较复杂的查询上(比如第九个查询)的性能提升会更加明显。
右边图中可以看到, JuiceFS 在充分缓存的情况下,存算分离的架构也能够做到和 HDFS 查询性能相当的程度。
所以综合来看,不管是元数据的性能测试,还是整体基于 TPC-DS 的查询测试,JuiceFS都能够做到性能和HDFS一致,同时相比对象存储有着大幅的性能优势。
四、Q&A
Q1: HDFS 不是存算分离架构是不是意味着会被淘汰?
A1: 我觉得从中短期来看的话, HDFS 肯定不会被淘汰,因为它还是大数据生态里面事实意义上的标准。只是在大数据平台上云之后,大家会重新思考在云上怎么搭建大数据平台的存储系统。有些公司可能还会选择把机房的整套基础设施都搬到云上,其实这样做在成本上可能会比在机房里还高一些,不过至少保证整体架构是经过验证的,但就达不到上云的初衷,没法更多地利用云的特性。如果还是把基于 HDFS 的存算耦合架构搬到云上其实没有得到任何云的好处,所以很多公司开始想要去选择对象存储或者类似JuiceFS的新一代存储系统也是基于这样一个原因。
Q2: S3 对象存储设计本身有 KV 设计的缺陷,那是不是也意味着淘汰?
A2: 是不一样的定位,因为 S3 最初的定位不是为大数据生态设计,它的定位是很清晰的:存储海量的非结构化数据,很容易扩展,可靠性足够高,数据不能丢,存储的成本要足够的低(所以它用了像 EC这样的技术)。这些都是 S3 的优势,只要有这样需求的场景,都会非常适用。但像大数据或者 AI 这类场景比较特别的一点在于,并不只是需要满足存储规模的需求就够了,还需要满足各种各样其他的需求,比如性能的需求,整体对于业务的影响等等我们认为更苛刻的需求,S3或对象存储是没有办法简单满足的,所以才需要有一些新的设计或者新的系统,怎么基于对象存储做进一步的改进和提升。S3 本身肯定是不会被淘汰的。
Q3: 存算分离架构,存算一体,哪个存储架构更适合哪些场景?
A3: 一般讲存算分离或者存算耦合,更多是基于你当下的整体架构。如果是考虑在云上搭建整个数据平台或者 AI 平台的话,天然地就会去考虑更多利用云上的资源。云上资源的弹性能力要最大化发挥必然是要去朝着存算分离这个方向走的。即使一开始可能是存算耦合的架构,长远来看还是需要去朝存算分离架构走。如果没有必要上云,而是在机房里有自己的一套私有云架构,用不用存算分离或者存算耦合,区别不大。更多还是怎么在存算分离的架构下发挥云的优势。
Q4: JuiceFS 和 Alluxio 的差异。
A4: 这个问题问得挺好。在 JuiceFS 官方文档里面有专门一篇比较 JuiceFS 和Alluxio,大家有兴趣可以去官方文档里看一下。简单讲一下,JuiceFS 的设计目标或定位是分布式文件系统,对标的是 HDFS 这类传统的分布式文件系统。我理解Alluxio的设计定位更多是一个缓存层,这个 缓存层的目标并不是实现完整的文件系统特性,比如不需要去完整地兼容 POSIX,不需要提供各种各样针对非大数据以外场景的特性。但是 JuiceFS 定位为分布式文件系统,所以我们需要考虑很多文件系统需要考虑的点。可能 Alluxio和 JuiceFS 在某些特性上(比如缓存)有一些重合的地方,不代表这两个项目的设计方向是一样的。
Q5: 存算分离架构效率比一体是不是要差一些。
A5: 如果只是简单地把存储和计算分离,不做任何优化,效率肯定是会变低的。即使到现在网络硬件技术已经发展得很好的情况下,可能还是没有本地的 I/O 效率高。所以只是简单地拆分存储和计算效率肯定会下降,因此才需要考虑存算分离之后怎么进一步地去优化性能。尽量不要降低业务或损害业务的一些日常指标。