元数据同步(metadata sync)是Alluxio的一个外围性能,它能使文件和目录与底层存储系统中的数据源保持一致,便于用户通过Alluxio获取最新数据。同时,理解外部过程对于性能调优也非常重要。本文介绍了Alluxio元数据同步性能的设计和实现。

在Alluxio中,元数据是指Alluxio文件系统中的文件和目录信息,包含所有者、组、权限、创立和批改工夫等信息。元数据独立于其内容,即便是空的文件或目录仍然领有关联的元数据。

Alluxio保护底层存储的文件系统或对象存储命名空间的正本。在Alluxio中,元数据的一致性十分重要,尤其是不同的集群在数据工作流中写入或读取数据,对底层存储的文件间接进行批改时,并不通过Alluxio。

上图是一个典型的场景,该数据工作流同时应用了Spark ETL和Presto SQL。ETL集群(未部署Alluxio)写入数据,而后由部署了Alluxio的剖析集群读取转换后的数据。因为Alluxio保护底层存储中的元数据正本,并对元数据进行治理,所以当底层存储的数据通过ETL步骤发生变化时,必须让剖析集群上的Alluxio实例感知到并与底层存储系统中的元数据保持一致,只有这样能力持续失常运行。

Alluxio在一个或多个存储系统下的对立命名空间中提供文件系统形象。通过Alluxio拜访文件或目录,会失去与间接拜访底层存储雷同的后果。例如,如果挂载到Alluxio根目录的底层存储是s3://bucket/data,那么在Alluxio中列出"/"目录的后果与在s3://bucket/data中列出对象的后果雷同,在Alluxio中打印"/file "会返回与s3://bucket/data/file同样的后果。

默认状况下,Alluxio将从底层存储按需加载元数据。在下面的例子中,从空(Empty)开始的Alluxio master在启动后不会有任何对于s3://bucket/data/file的信息。只有当用户在Alluxio中列出"/"目录或试图拜访"/file "时,这个文件才会被辨认。该“惰性”操作能够缩小不必要的工作并显著进步性能,因为底层存储中的元数据操作可能很慢。

留神,更新元数据能够是双向的。如果对文件系统的所有批改都通过Alluxio进行,那么Alluxio只须要扫描一次底层存储来检索初始状态,而后作为文件系统RPC调用的一部分,在Alluxio和底层存储中同步利用该批改,这将为用户提供统一的底层存储视图。但在事实中,对底层存储的批改通常在Alluxio外进行。因而,Alluxio master必须监控底层存储中文件和目录的减少、删除和更新,并在Alluxio文件系统中利用这些批改。同步两个命名空间的这一过程称为元数据同步。

当利用程序修改了Alluxio文件的元数据,并且该文件被长久化时,该批改总是会同步传输到底层存储,因而不须要触发元数据同步。当应用程序在Alluxio无感知的状况下更新底层存储文件时,有两种办法能够治理元数据的同步工夫。

基于工夫的主动同步

咱们能够将同步距离设置到Alluxio配置项“alluxio.user.file.metadata.sync.interval”上。

当该值为-1(以后默认值)时,Alluxio在初始加载后将永远不会与底层存储从新同步。

当该值设置为0时,每次拜访元数据,Alluxio都将与底层存储从新同步。

当该值为正时(默认单位为毫秒),Alluxio将(尽力)不在该工夫距离内从新同步门路。

留神,应用这种办法时,如果Alluxio中的某个门路从未被拜访过,将不会触发同步。一旦同步工夫距离过后该门路被拜访,Alluxio将再次与底层存储同步。例如,在Presto作业中,查问打算阶段会列出作业所需的所有文件,如果这些门路最近没有被拜访,则会触发同步。然而,该作业在后续阶段将不会同步,除非作业持续时间超过同步间隔时间。

因而,这种状况下,实践上说Alluxio可能会比同步工夫距离更频繁地从新同步。

咱们能够应用新的全局默认值(在alluxio-site.properties中设置)。或者在目录根底上配置该项,该配置会递归地作用在所有子文件和目录上。

应用LoadMetadata标记手动同步

如果因为同步工夫距离而没有进行元数据同步,则大多数Alluxio操作会持续应用Alluxio文件系统中以后的元数据,有一些例外值得一提:

对于大多数用户来说,Alluxio CLI "loadMetadata "是手动触发同步的最简略办法。例如,能够运行 "bin/alluxio fs loadMetadata /path/to/sync "来强制更新Alluxio门路"/path/to/sync "的元数据。

对于基于Alluxio文件系统SDK(Java)构建的应用程序,有两个API办法getStatus和listStatus能够检索门路或目录的元数据。在调用这些办法时,每次调用的选项中都会多出一个LoadMetadataPType字段,这可能会在被查问的Alluxio门路上触发master的“loadMetadata”过程。这一过程能够说是同步的简化版,只从底层存储加载文件元数据。但如果文件曾经在Alluxio中了,就不会批改文件的元数据。如果LoadMetadataPType被设置为NEVER,则不会加载任何内容,如果文件不存在,则会抛出FileNotFound异样。当LoadMetadataPType为ONCE时,只会为每个目录加载一次元数据。这只会影响两个文件系统的调用,并且仅在未产生同步时此选项才失效。

当Alluxio master收到RPC申请检索该门路的元数据时,Alluxio master可能会在Alluxio门路上触发元数据同步。此时不会有专用的服务来遍历整个文件系统的节点树(inode tree)并放弃同步,而是由master上每个独自的Alluxio文件系统操作来摊派这一工作。在RPC申请中同步的高级过程为:

步骤1:确定给出的Alluxio门路是否与相应的底层存储门路统一。如果不统一,意味着底层存储门路不存在,或者有与Alluxio不同的元数据。这一部分都是在解决该RPC申请的线程实现的。

步骤2:将步骤1填充到一个同步队列中,而后遍历同步队列,用一个线程池来解决这个队列里的每一个门路。遍历的程序是BFS(广度优先搜寻)程序,因为在处理过程中咱们会在队列末端不停增加新的门路。这种实现的并发度和executor(线程池)咱们将在并行度局部具体探讨。咱们有两个线程池解决不同的工作,一组线程叫做同步线程(“sync threads”),另一组叫做预取线程(“prefetch threads”)。队列的解决是由同步线程(“sync threads”)实现的,并应用预取线程向UFS读取底层存储信息。这样做是为了让网络I/O与计算同时进行。同步线程须要操作InodeTree,一旦咱们确定在之后须要某些文件的信息,就能够启动底层存储预取。预取线程将底层存储中文件的状态信息加载到一个底层存储的状态缓存中,这一过程将在缓存局部探讨。

请留神,如果元数据的同步过程在同步InodeTree某一部分的时候会阻塞其余对这一部分Inode的操作,这里的开销可能会很大。这是因为同步过程可能会对其正在更新的文件系统的元数据局部加写锁。当同步节点树中的特定门路时,RPC解决线程将首先获取整个文件门路上的读锁。因为同步线程也须要可能创立门路,因而也必须获取根门路的写锁。同步线程在解决根门路下的各个门路时,都会获取其余的锁。同步线程获取文件门路的写锁,并在门路处理完毕后立刻开释。

调度并行度

咱们能够通过管制三个配置参数来调整元数据同步的并行度。

alluxio.master.metadata.sync.concurrency.level 示意在单个元数据同步申请中(例如,在目录上)最多能够同时同步的文件数量。

alluxio.master.metadata.sync.executor.pool.size 示意所有同步操作的并发线程数。

alluxio.master.metadata.sync.ufs.prefetch.pool.size 示意在所有同步操作中能够执行底层存储预取操作的并发线程数。

缓存后果

为了进一步优化元数据同步的性能,Alluxio有三类不同的缓存,在元数据同步过程中有着不同的指标和用处。上面对这些缓存进行简略总结。

AbsentCache是负缓存(negative cache),用于防止查看已知不存在门路的底层存储。它应用前缀匹配来确定门路是否在底层存储中。例如,如果门路/a/b在缓存中,咱们就晓得/a/b/c在底层存储中肯定不存在。此外,AbsentCache条目附有工夫戳,这样咱们就能晓得它在底层存储中最初一次被查看的工夫。如果同步距离为一段时间,则工夫戳十分有用,能够依据它来确定是否须要从新查看底层存储中的文件或目录是否存在。

UfsStatusCache是用于在同步过程中预取底层存储状态的缓存。咱们通常能够在解决当前目录时预取一些文件的状态(应用预取线程),而不是在须要时获取门路信息。

UfsSyncPathCache是蕴含最近与底层存储同步门路的positive cache(正缓存)。当收到元数据操作时,咱们将查看该缓存,确定是否须要同步某一门路。

元数据同步是Alluxio最重要的性能之一,有多种办法能够触发同步,但应用时须要衡量对性能的影响。Alluxio master外部有一系列对元数据同步的优化。