亚马逊AWS官方博客

Alluxio 本地缓存 + EMR Presto 即席查询加速

背景 & 需求

对于大数据平台的 OLAP 查询引擎来说,天下武功唯快不破,“亿级秒开”是业务侧一直以来对技术部门的期望,Presto 做为大数据生态的业界知名的查询引擎和平台,首当其冲承载了业务查询分析的实时响应的需求。

对于 Presto 集群而言,横向扩展,加机器资源固然可以解决绝大多数性能问题,但成本也是客户考虑的重要因素,且大数据体系的实时数仓架构,即便在资源充足情况下,Presto 查询响应也会遇到各种挑战,比如:

  • 查询和访问的数据存在冗余重复,这对底层数据存储形成极大压力;
  • 慢 PRC 以及热点数据的问题,导致 Presto 查询性能不稳定;
  • 元数据存储及访问带来的 Presto schedule 开销;

解决方案概述

对以上场景而言,本地缓存技术会是很好的选择,可以挖掘服务器闲置资源的潜力。因为在 CPU 利用率的评估体系下,服务器的内存,本地磁盘可能有空闲资源,能够挖掘出一些可用资源为我们所用,可以在不增加机器成本的前提下有效达到我们的目的。

Alluxio 开源社区是大规模实践过的、有成功案例的分布式缓存方案,Presto 社区和 Alluxio 社区一道,开发了本地缓存的集成方案,已经在 Meta/Uber 等公司大规模落地实践。Alluxio 社区与 Amazon EMR 服务也有深入的交互和集成,官方提供了 on EMR 的集成方案,详见 Alluxio 社区文档,AWS 也提供了快速安装部署的 bootstrap 脚本及配置,详见 AWS 官方 blog

本文详细介绍了在 Amazon EMR 上集成 Alluxio 本地缓存做为 Presto 即席查询加速的具体实现,包括 Alluxio Presto 本地缓存整体架构原理,在 Amazon EMR 上 Alluxio 缓存相关的配置及对比测试。

架构 & 原理

Presto 和 Alluxio 社区共同开发的本地缓存的实现,是将 Alluxio 缓存嵌入到  Presto Worker 的进程中,使它整体归于 Presto Cluster 管理,这样相对于集群模式更加轻量级,如下图所示:

在配置启动 Alluxio 本地缓存后,Presto Worker 进程读取时将远程存储数据缓存在本地 Alluxio 文件系统(底层实现为 SSD 磁盘文件),如果后续查询已经存在 Alluxio 本地缓存上的数据,读取请求将直接从 Alluxio 本地缓存返回结果。

Alluxio 本地缓存支持 cache 到 Presto 的任务分配机制的亲和性(Affinity),通过一致性 hash 将 Worker 和 Cached 数据映射,Presto Worker 在 local 节点抽取 Alluxio 缓存数据,避免 shuffle 开销。

Alluxio 本地缓存支持自动对齐,查询覆盖,例如,假设 Presto 从偏移量 0 开始发出长度为 3MB 的读取,那么 Alluxio 缓存会检查磁盘上是否已经有 0-1MB、1-2MB 和 2-3MB 的块,并且只提取未缓存的块。

在 Presto Worker 上的 Alluxio 本地缓存基于 LRU 进行缓存过期和清除策略

Amazon EMR 提供了 Presto 的开箱即用,将 Alluxio 本地缓存集成到 Presto 的整体架构如下图所示:

  • Amazon EMR 通过 emrfs lib,Presto 支持 S3 的对象存储,因此当 Presto 调用时,会自动识别到 s3://的filesystem 文件系统路径的 emrfs implemention 实现,从而实现将 S3 的数据通过 Alluxio 本地缓存在 Presto worker 节点本地磁盘存储。
  • Amazon EMR 支持 Glue Catalog 数据目录,在 Amazon EMR 上的 Presto 的 hive catalog 配置项中,将 hive connector 的 metadata 指向 Glue Catalog,将缓存类型设置为 Alluxio,Presto 查询 Glue hive table 时即可触发本地缓存功能,从 S3 返回的查询结果自动更新/覆盖 worker 节点上的 Alluxio 缓存 bloc。

实施落地 Detail

以下我们详细介绍在 Amazon EMR 上启用 Alluxio 本地缓存的详细实施步骤和相关配置。

Alluxio 本地缓存改造及相关配置

Alluxio localCacheFileSystem,开源版本只支持 Alluxio uri ,不支持 S3,需要修改 Alluxio client 源代码,增加 S3 的 shcema 支持。

###Alluxio LocalCacheFileSystem 源代码
private static final Set<String> SUPPORTED_FS = new HashSet<String>() {
    {
      add(Constants.SCHEME);
      add("ws");
      add("s3");
    }
};


###修改后重新编译打包 alluxio-client-hdfs 的 jar
mvn  clean package -DskipTests  -Dmaven.test.skip=true -Dcheckstyle.skip=true -Drat.skip=true -Pjava8
tangqy@6c7e67c16c37 target % aws s3 cp ./alluxio-core-client-hdfs-2.7.3.jar s3://salunchbucket/
upload: ./alluxio-core-client-hdfs-2.7.3.jar to s3://salunchbucket/alluxio-core-client-hdfs-2.7.3.jar


###manually 替换 alluxio 发行版本中 client jar 中的修改的 class
cd /opt/alluxio/client/
sudo mkdir tmp
sudo cp alluxio-2.7.3-client.jar ./tmp/
sudo aws s3 cp s3://salunchbucket/alluxio-core-client-hdfs-2.7.3.jar ./tmp/
cd tmp && sudo jar -xvf alluxio-core-client-hdfs-2.7.3.jar

sudo jar -uvf alluxio-2.7.3-client.jar alluxio/hadoop/LocalCacheFileSystem.class
sudo jar -uvf alluxio-2.7.3-client.jar alluxio/hadoop/LocalCacheFileSystem$1.class 
  • Amazon EMR 新集群

对于 Amazon EMR 的新建集群,我们在 bootstrap 脚本中集成 Alluxio 安装部署时,替换 EMR 上 Presto client 开源版本的 jar 为修改后的版本,并重启 EMR 节点上的 Presto server,其示例脚本如下:

sudo aws s3 cp s3://salunchbucket/alluxio-2.7.3-client.jar /opt/alluxio/client/alluxio-2.7.3-client.jar
sudo rm -rf /usr/lib/presto/plugin/hive-hadoop2/alluxio-shaded-client-2.8.1.jar
sudo systemctl restart presto-server

##使用 steps 替换 emr 上 alluxio client jar
aws s3 cp ./modify_alluxio_client_jar.sh s3://salunchbucket/backup/modify_alluxio_client_jar.sh

其中 s3://salunchbucket/alluxio-2.7.3-client.jar 为修改后打包的 alluxio client jar,放在客户自己的 S3 存储桶中,/opt/alluxio/client/alluxio-2.7.3-client.jar 为在 EMR Presto Codinator 及 Worker 节点上安装部署 Alluxio 的对应路径。

  • Amazon EMR 已有集群

对于已经安装部署 Alluxio 的 Amazon EMR 集群,我们可以通过脚本方式,遍历 EMR 的节点,通过 ssh remote 执行替换并重启 presto 服务,其示例脚本如下:

#!/bin/bash
clusterId=$1
instancId=$2
pemKey=$3
declare -a EMR_NODES
aws emr list-instances —cluster-id $clusterId —instance-group-id $instancId —instance-states "RUNNING"|grep -i "PrivateIpAddress" > node.list
while read line
do
 ip=$(echo $line|cut -d':' -f2);
 EMR_NODES[++i]=$ip
done<node.list
echo "nodes:"${EMR_NODES[@]}

EMR_SSH_KEY=$pemKey
RESTART_INTERVAL=20

for node in "${EMR_NODES[@]}"; do
 node=${node//\"/}
 node=${node// /}
 echo "node=="$node
 ssh -o StrictHostKeyChecking=no -i $EMR_SSH_KEY -T hadoop@$node <<"EOF"

      sudo aws s3 cp s3://salunchbucket/alluxio-2.7.3-client.jar /opt/alluxio/client/alluxio-2.7.3-client.jar
      #重启 emr 节点上的 presto server
      sudo systemctl restart presto-server

EOF
 echo "RESTART presto server ON NODE [ ${node} ] complete"
done

其中 clusterId 为 Amazon EMR 集群 id,instanceId 是 Amazon EMR 实例组 id,pemkey 为 Amazon ssh 登陆密钥文件。

Alluxio 本地缓存 Amazon EMR 配置项

启用 Alluxio 本地缓存配置项主要有 cache type,base directory 等,详细配置可以参考如下:

{
    "Classification": "presto-connector-hive",
    "Properties": {
      "cache.alluxio.max-cache-size": "50GB",
      "cache.base-directory": "file:///tmp/alluxio",
      "cache.enabled": "true",
      "cache.type": "ALLUXIO",
      "hive.metastore": "glue",
      "hive.node-selection-strategy": "SOFT_AFFINITY",
      "hive.s3-file-system-type": "EMRFS"
    }
  }

其中:

  • “cache.type”:“ALLUXIO”指定本地缓存类型为 Alluxio
  • “cache.base-directory”:“file:///tmp/alluxio”,指定本地缓存的底层存储,该目录可以挂载 Amazon 高性能的 EBS 盘以便增大缓存查询及更新覆盖时的吞吐
  • “cache.alluxio.max-cache-size”:“50GB” , 指定整个 Presto 集群能够本地缓存的 Alluxio 数据量
  • “hive.node-selection-strategy”:“SOFT_AFFINITY”,开启本地缓存亲和度,local 缓存数据在同一个查询节点上

在 EMR console 控制台上配置和启用 Alluxio 本地缓存如下图所示:

注意:EMR 的 console 上修改 presto-connector-hive 不会重启 Presto worker,需要手动重启节点上的 Presto 服务,或者重新拉起集群时把 cache.enable 配置参数改为 false。

对比测试

这里我们使用 TPC utility 官方工具,生成通用的 tpcds benchmark 测试数据集,在 S3 上生成 200GB OLAP 测试数据,并使用 query99 为例做为对比测试基准。

关于 TPC Utiliy 的下载和使用这里不再赘述,感兴趣的小伙伴可以查阅 tpc benchmark 的官方站点

关闭 Alluxio 本地缓存下 query99 执行:presto-cli —catalog=hive —schema=tpcds_text_200 —files query99_local.sql

023-07-31T09:54:58.547Z        INFO    dispatcher-query-11     com.facebook.presto.event.QueryMonitor  
TIMELINE: Query 20230731_095411_00008_cmvrp :: Transaction:[be11b78e-eaf0-4f5a-a081-856c732ff457] :: 
elapsed 47346ms :: planning 1838ms :: scheduling 670ms :: running 4402ms :: finishing 40436ms :: begin 2023-07-31T09:54:11.170Z :: end 2023-07-31T09:54:58.516Z

开 Alluxio 本地缓存下 query99 执行:presto-cli —catalog=hive —schema=tpcds_text_200 —files query99_s3.sql

2023-07-31T09:58:00.945Z        INFO    dispatcher-query-26     com.facebook.presto.event.QueryMonitor  
TIMELINE: Query 20230731_095721_00010_cmvrp :: Transaction:[5b19310a-70f9-4795-90a2-94d9b653c3f8] :: 
elapsed 39555ms :: planning 362ms :: scheduling 551ms :: running 302ms :: finishing 38340ms :: begin 2023-07-31T09:57:21.363Z :: end 2023-07-31T09:58:00.918Z

从上面的日志对比,可以看出:在未开启本地缓存时,总耗时为 47.3 秒;在开启本地缓存后,总耗时为 39.5 秒。非常明显地提升了 Presto 的查询效率。 而 Presto 查询各阶段耗时对比统计,见下图所示。

结论及参考资料

综上所述,对于 EMR 中的 Presto 计算框架,在通过 Alluxio 提供了本地缓存的情况下,整体性能提升了约 20%。从 Presto Running 阶段的耗时对比来看,当 Presto 需要访问的数据位于本地时,可以以内存级的速度访问到数据;所以只在第一次访问数据时,从 S3 上读取一次。尤其对于生产环境中查询重复数据、热点数据的场景,会有更明显的效果。

如果您希望了解更多关于 AWS EMR 与 Alluxio 的资料,可以参考如下链接。

Alluxio 整体架构

Alluxio on EMR 快速部署

在 Amazon EMR 中利用 Alluxio 的分层存储架构

EMR Alluxio 集成 detail

Flink Temporal Join 详细

本篇作者

唐清原

亚马逊云科技高级解决方案架构师,负责 Data Analytic & AIML 产品服务架构设计以及解决方案。10+数据领域研发及架构设计经验,历任 IBM 咨询顾问,Oracle 高级咨询顾问,澳新银行数据部领域架构师职务。在大数据 BI,数据湖,推荐系统,MLOps 等平台项目有丰富实战经验。

张盼富

AWS 解决方案架构师,从业十三年,先后经过历云计算、供应链金融、电商等多个行业,担任过高级开发、架构师、产品经理、开发总监等多种角色,有丰富的大数据应用与数据治理经验。加入亚马逊云科技后,致力于通过大数据+AI 技术,帮助企业加速数字化转型。