乐趣区

关于云存储:JuiceFS-在数据湖存储架构上的探索

大家好,我是来自 Juicedata 的高昌健,明天想跟大家分享的主题是《JuiceFS 在数据湖存储架构上的摸索》,以下是明天分享的提纲:

首先我会简略的介绍一下大数据存储架构变迁以及它们的优缺点,而后介绍什么是 JuiceFS,其次的话会再重点介绍一下对于 JuiceFS 和数据湖的一些联合和关联,最初会介绍一下 JuiceFS 和数据湖生态的集成。

大数据存储架构变迁

纵观整个大数据存储架构的变迁,能够看到有非常明显的三个阶段:第一个阶段就是从最早的 Hadoop、Hive 等我的项目诞生之后,有了 数据仓库 (Data Warehouse)的概念。随着数仓的逐渐倒退,同时有了云的诞生,对象存储的诞生,以及大数据与 AI 的时代到来之后, 数据湖 (Data Lake)这个概念就被凸显了进去。最近两三年有一个新的概念,或者是说到了一个新的阶段叫做 湖仓一体(Lakehouse)。传统数仓大家都比拟理解,明天会着重看一下前面这两个阶段,也就是数据湖和湖仓一体。

为什么要有「数据湖」?

数据湖很重要的一个诞生契机,其实是解决数据孤岛(Data Silos)问题。产生数据孤岛的根本原因,来自于不同的业务或者不同团队,因为一些历史起因造成了数据之间其实是一个孤岛或者相互之间没有方法去做连贯。

随着不同业务的引入,在企业外部数据的格局会变得越来越多样,除了最早的传统的结构化数据以外,会发现还有很多半结构化的甚至是非结构化的数据。这些半结构化和非结构化数据也心愿能逐渐引入到整个公司的数据管理或者运维外面来,传统数仓的架构或者说存储的模型此时就没有方法去满足这种多样性的数据格式的存储需要。

而后第三点是扩散的数据管理,这点其实是跟第一点数据孤岛也是有关联的。因为你的数据是散布或者扩散在很多不同的中央的,数据的治理或者一些权限的管制上,也会绝对的扩散。这个时候你如果要去针对不同的业务与不同的团队去做治理,也会是一个比拟大的工作量。

第四点是存储与计算的耦合(简称「存算耦合」),也是跟传统 Hadoop 的架构无关,传统的像 HDFS、YARN 的架构,是针对存算耦合架构来设计的,但在对于当初基于私有云的大数据架构来说,这种存算耦合的架构就比拟不足弹性了,不论是在运维的弹性上,还是对老本的管制上。

最初一点随着 AI 行业的倒退,在机器学习或深度学习这块的数据退出进来之后,也是心愿可能在数仓或者说整个大数据架构外面为基于机器学习或深度学习的业务提供更好的反对。不仅是存储数据,例如还须要对接深度学习的框架,所以就要提供一些接口的反对,比方 POSIX 等对算法工程师更敌对的形式,而不是传统的通过 SQL 或一些其它的形式来提供给业务团队。

什么是「数据湖」?

这里援用维基百科上的一句简介:

A data lake is a system or repository of data stored in its natural/raw format, usually object blobs or files.

其中一个比拟重要的定义是 natural/raw format(原始格局),跟传统数仓比拟大的区别是咱们会偏向于把数据以原始的格局先存到数据湖外面来。数仓其实也还是存在的但它是一个后置的过程,为了实现这样一个数据湖,最基本的是须要一个足够便宜且可能反对海量数据规模的底层存储。目前看下来在云上的话,对象存储是一个十分好的抉择,它既做到了便宜牢靠,同时也可能反对海量的数据。但对象存储也不是一个相对的计划,前面会具体地去做一些比拟。

简略来说就是「Everything in one place」,意思是所有数据都先放到数据湖外面来,你要做数仓也好,做一些其余的后置 ETL 也好,那是下一个阶段的事件,但前提是要把所有的数据都放在一起。「后置 ETL」的意思是说 ETL 仍然存在也仍然须要,只是它变成了一个后置的流程。因为用到了对象存储,以及存算拆散的架构,所以在整个的架构设计上也会更加的云原生。

为什么要有「湖仓一体」?

在整个数据湖的架构外面数仓仍然是存在的,然而它在整个 pipeline 的阶段被后置了,必然就会带来一些数据的滞后。同时传统的像 Hive 这些组件,其实你要做到近实时或者基于 Hive 来做增量的数据更新是比拟麻烦的,特地是如果你要把分区(partition)的工夫窗口缩得很短的话。

之前提到的机器学习和深度学习的联合问题,在数据湖阶段也还是存在。尽管有了数据湖,但对于整个深度学习这块的反对也还是不太够,所以在湖仓一体这个阶段仍然是须要解决的一个问题。

而后就是数据反复拷贝和反复 ETL,因为 ETL 是后置的,数仓也是后置的,所以有很多数据有可能是会从湖外面再同步或复制到数仓外面,就会带来一些数据的反复拷贝或者反复 ETL,反复两次甚至三次都有可能。

最初就是基于对象存储这样的存储类型,心愿可能提供更多高级个性的反对,比方 ACID 事务、多版本数据、索引、零拷贝克隆等。

什么是「湖仓一体」?

湖仓一体有一些要害的因素,其中第一个是须要一个对立凋谢的底层文件格式,这个格局比如说能够是 Parquet、ORC 等业界公认的格局。第二点咱们须要一个凋谢的存储层,具体来讲是相似 Delta Lake、Iceberg、Hudi 的一些开源组件。第三点是要有凋谢的计算引擎集成,不论你应用哪一种存储,都须要可能反对下面多种多样的计算引擎,而不是把用户或者业务团队限定在某一个引擎外面,不论用 Spark 也好,Presto 也好,用其它的商业引擎也好,能够做到多样化的反对。最初一点就是和深度学习框架的联合,这里拿 Uber 开源的 Petastorm 我的项目举例,Petastorm 是为 TensorFlow、PyTorch 等框架提供 Parquet 格局读写反对的组件,目前初步做到了一些对深度学习框架的反对。

JuiceFS 简介

JuiceFS 一个开源的云原生分布式文件系统,为云环境设计,提供齐备的 POSIX、HDFS 和 S3 API 兼容性。应用 JuiceFS 存储数据,数据自身会被长久化在对象存储(例如 Amazon S3),绝对应的元数据能够按需长久化在 Redis、MySQL、TiKV 等多种数据库中。
目前在 GitHub 上曾经有超过 5000 个 star,也有超过 50 个内部贡献者来一起参加这个我的项目的保护。

JuiceFS 从架构设计上来说,更偏向于凋谢联合的态度。家喻户晓文件系统外面最重要的一个组件就是元数据引擎,JuiceFS 心愿可能联合已有的开源我的项目,比如说 Redis、SQL 数据库、分布式 KV 等,把它们纳入进来作为整个 JuiceFS 架构外面的一个组件。在数据存储方面,目前 JuiceFS 也曾经反对超过 30 种底层的存储系统,除了最次要的对象存储,还反对像 Ceph、MinIO、Ozone 这样开源的组件。同时 JuiceFS 也是一个跨平台的组件,在 Linux、macOS、Windows 上也都能够间接原生的运行。

在 Kubernetes 的环境里,JuiceFS 提供了原生的 CSI Driver,能够间接通过 Kubernetes 的 PV 或 PVC 的形式间接 mount 到 pod 里。最初就是一些更高级的个性,比如说数据缓存、加密、压缩、回收站、配额等,目前 JuiceFS 的开源社区里也有很多的团队和公司曾经在生产环境中应用,例如小米、现实汽车、Shopee、知乎、火山引擎、网易游戏、携程等。

上图次要分了三块,一个是 Metadata Engine 也就是文件系统的元数据引擎,所谓元数据引擎就是要存储整个文件系统的元信息,比方文件的名字、大小以及权限信息和目录构造等。这里 JuiceFS 更心愿和一些成熟的开源的并且大家日常会应用到的数据库做联合,所以上图列举了一些罕用的数据库,都能够作为 JuiceFS 的元数据引擎。

Data Storage 局部是 JuiceFS 底层须要依赖的一个数据存储,咱们没有反复造轮子,而是抉择了站在已有存储的肩膀上。云上的话对象存储是一个十分好的基础设施,大家都晓得它有很多益处,例如低成本、高吞吐、高可用性。如果你是在 IDC 或者机房外面也能够有相似的基础设施提供,JuiceFS 作为应用方,能够间接把这些 Data Storage 对接上,并原生地把它作为整个文件系统的底层数据存储。

最下面的话就是客户端(Client),也就是 JuiceFS 的用户会间接接触到的这部分。通过不同的接口,让用户在不同的环境与不同的业务外面都能够拜访到 JuiceFS,用户不必放心在不同的应用环境下会呈现一些不统一的状况,只须要关怀用哪个最相熟的接口去拜访就好了。

上图展现的是一个文件通过 JuiceFS 最终存储到对象存储上的一个流程,JuiceFS 会对一个文件做三个级别的拆分,就是最左边这一列的 Chunk、Slice、Block 三个级别。

首先 JuiceFS 默认会依照固定的 64MB 大小,把一个文件依照这个粒度来拆分成很多的 Chunk,而后每个 Chunk 外部的话又可能会有很多这种不同个数、不同长度的 Slice 来形成,每个 Slice 最终又会由很多定长的 Block 来形成。Block 的大小用户是能够配置的,默认状况下举荐应用 4MB 作为 Block 的大小。最终 Block 通过可选的比方压缩或者加密之后,再上传到对象存储外面,所以如果你间接去看对象存储里存储的数据的话,是不会看到原始文件的。比如说你的原始文件可能是 1G 大小的文件,但其实在对象存储下来看的话,会看到很多小的 4MB 的 Block。

须要特地指出的一点是如果文件自身就是小于 4MB 的,比方一张图片只有 100KB,这时 JuiceFS 是不会补齐到 4MB 的,还是会依照它原始的大小,文件是 100KB 最终存储到对象存储上也还是 100KB,不会补齐,不会占用额定的空间。

最初讲一下 JuiceFS 为什么要对文件存储格局去做分级。首先是须要基于对象存储来反对一些高级的个性,比如说随机写入;其次对于不同的读写访问模式,通过分块之后也能够晋升性能,比如说在并发写入或并发读取上可能做到更好的性能优化。

JuiceFS 与 HDFS、对象存储的比拟

从存储规模上来说,其实大家都晓得 HDFS 的 NameNode 在单 namespace 上是有存储下限的,一般来说到亿级别这个量级就差不多了,但如果你要存储更多的数据,你可能要做 ​​federation 或者说一些其它的形式去扩大。对于对象存储和 JuiceFS 来说,是能够十分轻松的撑持到百亿级甚至更大的存储规模。

而后在一致性上对于文件系统来说,之前不管应用 HDFS 或者说其它的文件系统,默认状况下,都是心愿文件系统提供的是强一致性的保障。然而因为对象存储的衰亡之后,会发现最终一致性反而会是一个更常见的状况。不过目前也有一些对象存储,比方 S3 曾经反对了强一致性。

在容量治理上 HDFS 是须要手动扩缩容的形式,所以你没有方法在云上做一个十分弹性的容量治理,然而反观对象存储和 JuiceFS 的话,在容量治理上是能够做到十分弹性的,按量付费,大幅节约了存储老本。

其它几个个性对于大数据场景也是比拟要害的,比如说原子重命名、List 性能、随机写、并发写等,这些个性对于传统的 HDFS 都是默认反对的,但对于对象存储来说,有些个性它是局部反对的,有些个性齐全无奈反对。因为 JuiceFS 自身是一个齐备的文件系统,所以这些个性都是具备的。

缓存减速这块,其实在 HDFS 或对象存储上目前都还是不具备的一个性能,须要联合一些内部组件来实现,但 JuiceFS 自身曾经内置了这个个性。

最初就兼容性来说,对象存储能够用一些社区的组件去通过 HDFS 的 API 拜访,但目前临时无奈做到齐全的兼容。包含 POSIX 这块,尽管你能够用到如 S3FS 或者一些其它组件以 POSIX 的接口来拜访对象存储,但它也只能达到一个局部兼容的状态。对于 JuiceFS 来说是齐全兼容 HDFS 和 POSIX 接口的。

这里咱们拿 HDFS 外面的一个组件 NNBench 做了元数据的性能比拟,上图比照的是元数据申请提早,越低越好。能够看到对象存储与 HDFS、JuiceFS 来相比的话,在提早上是能够差到一个甚至多个数量级的,这个也很好了解,元数据申请对于对象存储来说自身就是比拟大的开销。反过来看 JuiceFS 和 HDFS 的话,其实是能够做到旗鼓相当的性能体现的。

另一个比照是元数据申请吞吐,越大越好,在某些场景下 JuiceFS 甚至能够做到相较 HDFS 有更好的性能体现,而对象存储则会相差很多。

JuiceFS 与 Lakehouse

通过观察 Lakehouse 的特色,咱们首先发现 Lakehouse 对于文件系统的依赖仍然是存在的,如上文提到的 List 性能、原子重命名、并发写、强一致性等。其次,对象存储在应用上是有一些限度的,比方对象存储基于 key 前缀的申请限度,也包含对象存储的 API 申请是有老本的,特地是在大数据场景,API 申请老本还是蛮高的。最初就是缓存减速对于性能的影响。

Lakehouse 对文件系统的依赖

首先咱们看一下 Lakehouse 对于文件系统的依赖。这里能够看上面这个表格,这个表格是间接从 Hudi 的官网文档外面摘抄过去的,Hudi 社区之前统计过间接用对象存储,并依据不同的文件规模或者文件数来做 List 的性能比拟。

能够看到从 100 到 100K,随着文件数的增多,整个对象存储 List 的开销是逐渐增大的,到前面曾经变成了线性增长。所以在治理大量文件或者数据时,List 的性能开销无奈漠视。

反过来看 HDFS 或 JuiceFS 这类有独立的元数据管理的文件系统,List 申请的开销其实是十分小的,能够做到毫秒级甚至更快,微秒级都是有可能的。正因为整个文件系统元数据管理对 List 十分敌对,在一个很短的工夫内就能够实现整个目录的 List。还有更多文件系统独有的个性,比方原子重命名、并发写、强一致性等都是十分要害的个性。

Specifically, Delta Lake relies on the following when interacting with storage systems:

  • Atomic visibility: There must a way for a file to visible in its entirety or not visible at all.
  • Mutual exclusion: Only one writer must be able to create (or rename) a file at the final destination.
  • Consistent listing: Once a file has been written in a directory, all future listings for that directory must return that file.

下面这段话是从 Delta Lake 的官网文档上摘抄过去的,在这里就着重提到了对 Delta Lake 来说它依赖底层的存储系统须要具备的几个个性,比如说原子性,其中就包含并发写、原子重命名等,而后是一致性的 Listing,这是对于文件系统强一致性的要求。同样的,以上这些个性对于不论是 Hudi 或者 Iceberg 来说也都有相似的需要。所以对于文件系统的个性需要,在 Lakehouse 的组件上都属于一个隐性的,或者说最根本的依赖,如果对象存储或其它零碎满足不了某些个性的话就会带来一些限度。比如说 Delta Lake 在用 S3 的时候,尽管能够并发读数据,然而无奈反对并发写,只能在单个 Spark driver 里写数据来保障事务。

对象存储的 API 申请限度和老本

提到对象存储的 API 申请限度和老本的话,这里咱们以 S3 为例,在 AWS 官网文档上其实也曾经明确告知用户,针对每个 prefix(这里 prefix 的定义就是存储到 S3 上的每一个对象的 key 的前缀)的 GET 申请最大 QPS 是 5500,PUT 申请的最大 QPS 是 3500,对于惯例利用而言这个申请限度其实是没问题的,然而对于大数据场景来说,QPS 限度就会影响整体计算工作的性能甚至是稳定性了。对此 Iceberg 提出了一个优化办法是在 key 的最后面加上一个随机的哈希值,目标就是为了扩散申请的 prefix,使其不会那么快地触碰到针对单个 prefix 的 QPS 限度。

对于 JuiceFS 来说,设计上曾经人造具备扩散申请 prefix 的理念。因为所有的文件数据最终上传到对象存储的时候,都会被切分成 4MB 的 block。每个 block 在对象存储上的 key 其实是一个多级的 prefix 来形成,它不是一个单级的目录构造。比如说 0/1/123_0_1024 这个 key,是依据每个 block 的 ID 做了两级 prefix,而后不同的 block 会扩散到不同的 prefix 外面来。

而后对于同一个文件来说,如果它是个大文件的话,它的所有 block 也是散布在不同的 prefix 外面的。所以尽管看起来是拜访同一个文件,然而对于对象存储来说你拜访的是不同的 prefix,所以这也是 JuiceFS 给文件分 block 的益处,也是对于对象存储 API 申请限度的一个优化设计。

其次在对象存储申请老本上,就 JuiceFS 而言,对于对象存储 API 的依赖其实非常少,只有 GetObjectPutObjectDeleteObject 这三个 API,剩下的所有 API 都不依赖。所以接入 JuiceFS 数据存储的存储系统,只须要提供这三个 API 就够了,所有的元数据申请都不会通过对象存储,这部分的 API 申请老本就省掉了。

刚刚提到这三个 API 其实次要是用来读写或者删除数据用的,其中比方 GetObject 是能够通过前面会提到的「缓存减速」做进一步的优化,JuiceFS 会主动地把频繁拜访的数据缓存到本地,这样可能大幅缩小热数据对于对象存储 API 申请的依赖。相比间接拜访对象存储,API 申请老本会升高很多。

缓存减速

第三点就是刚刚提到的缓存减速,这里咱们拿一个 benchmark 为例,这个 benchmark 应用业界最常见的 TPC-DS 数据集,计算引擎用的是 Presto,数据采纳了两种格局,别离是 ORC 和 Parquet。

能够看到在缓存充沛预热的状况下,JuiceFS 的整体性能体现是能够做到与 HDFS 相当的,所以这也是缓存减速可能体现的一些劣势,特地是在存算拆散的架构下。

JuiceFS 与数据湖生态

首先 JuiceFS 社区给 Hudi 奉献了一个 PR 能够在 Hudi 内原生反对 JuiceFS,这个个性曾经在 Hudi v0.10.0 及以上版本反对。具体应用办法能够参考 Hudi 的官网文档。这里只是拿 Hudi 举了个例子,其实用 Iceberg、Delta Lake 联合 JuiceFS 也是相似的,JuiceFS 自身曾经提供了 HDFS 齐全兼容的 API,任何应用 HDFS 的中央都能够间接替换为 JuiceFS。

另外 JuiceFS 跟 AI 社区一个比拟风行的开源组件 Fluid 也有一些联合。Fluid 是一个开源的以 Kubernetes 环境为主的数据集编排以及拜访减速的组件。目前它次要用在 AI 的场景,然而其实整个 Fluid 社区也想要跟大数据场景做一些联合。Fluid 次要由阿里云的团队以及南京大学的一些团队来保护和开发,它也是 CNCF 里的一个沙盒我的项目。

JuiceFS 社区和云知声团队一起给 Fluid 社区奉献了一个 PR,把 JuiceFS 作为一个 runtime 集成到 Fluid 中。如果用 Fluid 来做 AI 模型训练,就能够间接原生地应用 JuiceFS 作为其中的一个后端存储或者说减速组件,帮忙你更快地在 Kubernetes 里把模型训练任务跑起来。大家有趣味的话,能够查看 Fluid 的官网文档理解一下。

以上就是我明天的分享,感激大家!

如有帮忙的话欢送关注咱们我的项目 Juicedata/JuiceFS 哟!(0ᴗ0✿)

退出移动版