Alluxio技术内幕:高性能的异步读缓存

概览

Alluxio服务通过连接底层持久化存储并按需将数据缓存至内存中,为不同的应用作业提供了一个可以高速并发访问的数据层。在Alluxio中,每一个文件根据其大小在逻辑上被划分成不同的“块”(默认512MB)。块在Alluxio中是缓存的最小单位。当Alluxio客户端用从Alluxio服务中读取文件时,如果被请求的数据块并未缓存在Alluxio中,则会触发缓存操作。将数据缓存在Alluxio空间后可以极大地提高后续分析作业的性能。

在Alluxio v1.7之前,当客户端向Alluixo请求读的数据没有命中缓存时,Alluxio客户端会默认地在读数据的时候同步地执行缓存操作。但当应用程序第一次读取一个文件并且仅需要读取该文件的一小部分的时候,这种同步缓存的操作可能对应用程序的性能造成损失。具体地说,在缓存没有命中的情况下,Alluxio客户端会(通过worker)去底层存储系统读取一个完整的数据块,将其缓存至Alluxio,同时把所需数据返回给应用程序。例如,对于很多SQL类型的作业,客户端常常只需要读取Parquet文件的表尾(不超过数MB大小),但第一次请求Alluxio时,Alluxio会尝试读取并缓存一个完整的数据块,以便于响应未来的请求。

Alluxio v1.7开始实现了优化的异步数据缓存操作。客户端不再同时负责数据的读取和缓存,而是将缓存操作交给worker节点与客户端的读操作异步进行。这一改进极大的简化了应用程序客户端在数据读取时的角色,并且由于客户端不等待缓存操作完成,可以显著提高某些类型作业的性能。本文解释了Alluxio v1.7及更高版本中的数据缓存操作,并对如何最大化地利用该功能提供了建议。本文涵盖的主题包括:

  • 异步缓存策略
  • 调整和配置异步缓存
  • 异步缓存的优点

异步缓存策略

异步缓存将缓存操作的开销由客户端转移到worker。客户端读数据的同时,缓存数据块的任务被交给worker在后台异步来处理(除非用户指定读取类型为”NO_CACHE”)。不论是读取完整或部分数据块,缓存操作对客户端性能均没有额外的影响,用户也不再需要像在Alluxio 1.7以前那样设置参数“alluxio.user.file.cache.partially.read.block”来启动或者关闭对只读了一部分的数据块的缓存。

Worker内部也利用帮客户端读取底层存储的过程中获取的数据做出了优化:如果客户端使用读取类型”CACHE”,从头到尾顺序从底层存储读取一个完整数据块,那么worker会在帮助客户端读底层存储系统的过程中积累了完整的数据。因此在客户端读顺序取完一个数据块以后,worker就可以直接缓存该数据了。

图1显示了读取数据块过程中的这一同步优化的部分。客户端只读取所需数据(步骤1,2,4,5),而worker在读取的时候顺便缓存(步骤3)。 当客户端读取完整的块,则将整个数据块会被缓存(步骤6)。

读取数据过程中的同步部分

但如果worker发现,客户端只是读取数据块的一部分,或者正在以非顺序的方式读取数据块内部数据,那么worker便会放弃在读的时候顺便缓存。而客户端则会在读取完成后向worker节点发送异步缓存命令并继续。之后worker节点再从底层存储获取完整的块。

如图2所示,在客户端和worker节点间的异步缓存请求使用轻量级RPC通信(步骤1)。在worker确认请求后(步骤2),客户端可以立即继续运行,而步骤3和4可以在worker后台异步进行。

读取数据块后的异步缓存

调整和配置异步缓存

Alluxio worker节点在后台并行执行异步缓存,同时也服务于来自客户端的同步读取请求。每个worker节点有一个线程池,其大小由参数“alluxio.worker.network.netty.async.cache.manager.threads.max”指定。 参数默认值为8,这意味着worker最多可以使用8个核从其他worker或底层存储系统下载块,并在本地缓存以供将来使用。可以调高此值以加快后台异步缓存速度,但CPU使用率会增加。 降低该值则会减慢异步缓存速度,同时也释放了CPU资源。

异步缓存的优点

这里我们提供一个简单的例子来展示异步缓存的好处:用户需要先从作为底层存储系统的S3中读取某一文件的一小部分,但之后还会有更多的读取请求,因此还是打算缓存整个文件。(1) 使用异步缓存之后,从S3中读取文件的前5KB只需要大约几秒钟,客户端从而可以在下载完5KB后立即返回。(2) 而对于Alluxio 1.7之前版本中,由于要缓存整个块,第一次读取这5KB数据将花费大约几分钟(速度将取决于网络连接条件)。在这两种情况下,数据块在初始请求后的几分钟内将完全缓存到Alluxio。但使用异步缓存后,客户端在初始读取所需的数据后可以继续执行,同时可以在worker后台缓存完整数据块。

异步缓存极大地提高了工作负载冷读取的性能,它们不需要完整顺序地读取数据块,比如在Presto或SparkSQL等计算框架上运行SQL工作负载。使用异步缓存,第一次查询将和直接从连接存储读取数据花费相同的时间量,并且随着数据异步缓存到Alluxio中,集群的整体性能将逐渐提高。

未来工作

管理Alluxio存储是Alluxio系统的一个重要部分。 在将来的版本中,异步缓存机制将得到进一步改进,包括:

  • 删除被动缓存的概念(ALLUXIO-3136)
  • 更细粒度地控制资源使用情况,如网络带宽,异步缓存(ALLUXIO-3137)
  • 改进worker间的数据传输机制(ALLUXIO-3138)
  • 优化异步缓存数据读取(ALLUXIO-3141)