亚马逊AWS官方博客

AWS Graviton3 加速 Spark 作业执行:Benchmark

AWS 于 2022 年 5 月宣布推出 Graviton3,使用 ARM Neoverse 内核定制设计的 ARM 架构构建,针对提供高性能和能效进行了优化,与 Graviton2 相比,AWS Graviton3 的计算性能提高了 25%;并于 2023 年 11 月 16 日宣布在中国区域推出 C7g、M7g 和 R7g 实例

客户在评估将他们的大数据工作负载转移到 AWS Graviton3 的过程中,许多人询问,在典型的批处理作业引擎 Spark 工作负载中,是否可以实现比同等配置 x86 实例更具性能和成本优势。

在这篇文章将比较 5 种相同配置的 EC2 实例在执行 TPC-DS Benchmark 时的性能和成本。

性能对比:

  • r6g.2xlarge 相比 r5.2xlarge,完成时间上升 5%
  • r7g.2xlarge 相比 r5.2xlarge,完成时间节省 22%
  • r7g.2xlarge 相比 r6i.2xlarge,完成时间节省 1%

成本对比:

以上述 5 种实例在 us-east-2 区域的 1 年期无预付 RI 价格的有效小时费率为基础,计算执行 TPC-DS Benchmark 所需的成本:

进一步,以 r5.4xlarge 的 Benchmark 成本为基准进行归一化处理,得到如下比较结果:

其中:

  • r6g.2xlarge 相比 r5.2xlarge ,成本节省 16%
  • r7g.2xlarge 相比 r5.2xlarge ,成本节省 30%
  • r7g.2xlarge 相比 r6i.2xlarge ,成本节省 17%

本文后续内容将详细描述如何搭建 Spark Local 模式集群和执行 TPC-DS Benchmark 测试。

Benchmark 工具介绍

TPC-DS 是一个由 Transaction Processing Performance Council(TPC)组织发布的决策支持基准测试。它旨在模拟现实世界中的决策支持系统的工作负载和查询模式,用于评估数据仓库系统的性能和功能。

TPC-DS 具有以下主要特点:

  • 数据模型和查询

TPC-DS 定义了一个复杂的数据模型,包括 7 个事实表和 17 个维度表,涵盖了零售产品供应链的各个方面。它还提供了 99 个预定义的 SQL 查询,涉及各种决策支持场景,如销售分析、库存管理、促销效果评估等。

  • 数据规模

TPC-DS 支持多种数据规模,从 1GB 到 100TB 不等,以满足不同测试需求。数据是由数据生成器根据一定规则生成的,具有很好的可扩展性。

  • 并发测试

TPC-DS 支持并发测试,模拟多个用户同时运行查询的场景,更贴近真实的数据仓库工作负载。

  • 多种测试指标

TPC-DS 定义了多种性能指标,包括查询响应时间、综合查询吞吐量(QphDS)、功率测试等,全面评估系统的性能表现。

  • 可移植性

TPC-DS 基准测试具有良好的可移植性,可以在不同的数据库管理系统和硬件平台上运行,方便进行跨平台性能对比。

TPC-DS 基准测试被广泛应用于数据仓库系统的性能评估和优化,用户可以更好地了解数据仓库系统在真实场景下的性能表现,为系统选型和优化提供依据。

Benchmark 组网

在本文采用 r7g.4xlarge 搭建 Spark Local 集群并执行 TPC-DS Benchmark。

  • 节点数:1 * r7g.4xlarge
  • EBS 磁盘:200GB GP3(500MB/s 吞吐,10,000 IOPS)
  • 操作系统:Amazon Linux 2
  • 主要软件版本:
    • Open JDK 1.8
    • Scala 2.12.18
    • Hadoop 3.3.1
    • Hive 3.1.3
    • Spark 3.3.1

部署 Spark 单节点

创建 EC2 实例

本次 Benchmark 中使用单节点 Spark  Local 模式,所有软件均部署在该节点。您可以参考教程:Amazon EC2 Linux 实例入门,完成这台 EC2 实例的创建。

安装基础软件包

通过 SSH 登录到 EC2 实例,使用 ec2-user用户,执行下面命令安装必要的基础软件。

提示:整个安装配置过程可能耗时较长,建议通过 screen 或 tmux 等工具创建一个新的会话执行后续过程。

设置软件栈版本:

echo "export HADOOP_VERSION=3.3.1" >> ~/.bashrc
echo "export HIVE_VERSION=3.1.3" >> ~/.bashrc
echo "export SPARK_VERSION=3.3.1" >> ~/.bashrc
echo "export SCALA_VERSION=2.12.18" >> ~/.bashrc

安装 OpenJDK:

sudo amazon-linux-extras install -y epel
sudo yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel git gcc gcc-c++ patch htop dstat nload
JAVA_HOME="/usr/lib/jvm/jre"
echo "export JAVA_HOME=${JAVA_HOME}" >> ~/.bashrc
echo "export PATH=${JAVA_HOME}/bin/:${PATH}" >> ~/.bashrc
source ~/.bashrc
java -version

安装 Scala:

cd ~
wget https://downloads.lightbend.com/scala/${SCALA_VERSION}/scala-${SCALA_VERSION}.tgz
tar zxf scala-${SCALA_VERSION}.tgz
ln -s $HOME/scala-${SCALA_VERSION} scala
echo "export SCALA_HOME=$HOME/scala" >> ~/.bashrc
echo "export PATH=$PATH:$HOME/scala/bin" >> ~/.bashrc
source  ~/.bashrc

安装 Maven:

wget https://dlcdn.apache.org/maven/maven-3/3.9.6/binaries/apache-maven-3.9.6-bin.tar.gz
tar zxf apache-maven-3.9.6-bin.tar.gz
ln -s ~/apache-maven-3.9.6 maven
MAVEN_HOME="/home/ec2-user/maven"
echo "export MAVEN_HOME=${MAVEN_HOME}" >> ~/.bashrc
echo "export PATH=${PATH}:${MAVEN_HOME}/bin" >> ~/.bashrc
source ~/.bashrc
mvn -v

安装 mysql 客户端,主要用于 Hive 组件:

wget https://repo.mysql.com/mysql80-community-release-el7-7.noarch.rpm
sudo rpm -Uvh mysql80-community-release-el7-7.noarch.rpm
sudo yum install -y mysql-community-client mysql-community-server --nogpgcheck
sudo systemctl start mysqld
sudo systemctl status mysqld
# 修改 MySQL 数据库 root 用户初始密码
cat << EOF > create_remote_login_user.sql
alter user 'root'@'localhost' identified with mysql_native_password by 'DoNotChangeMe@@123';
set global validate_password.policy=0;
alter user 'root'@'localhost' identified with mysql_native_password by 'gv2mysql';
create user 'root'@'%' identified with mysql_native_password by 'gv2mysql';
grant all privileges on *.* to 'root'@'%' with grant option;
flush privileges;
EOF
MYSQL_INIT_PASSWORD=$(sudo grep 'temporary password' /var/log/mysqld.log | tail -1 | awk '{print $NF}')
MYSQL_CMD_OPTIONS="--connect-expired-password -uroot -p${MYSQL_INIT_PASSWORD}"
mysql $MYSQL_CMD_OPTIONS < create_remote_login_user.sql
# 验证登录
mysql -uroot -p'gv2mysql' -e "show databases;"
# 创建 MySQL 数据库的 hive 用户
cat << EOF > create_hive_user.sql
CREATE DATABASE hive;
USE hive;
CREATE USER 'hive'@'localhost' IDENTIFIED BY 'Hive313Mysql';
GRANT ALL ON *.* TO 'hive'@'localhost';
FLUSH PRIVILEGES;
EOF
mysql -uroot -p'gv2mysql' < create_hive_user.sql

安装和配置 Hadoop 软件

首先确定实例的架构(x86_64 或 aarch64),后续会下载对应架构的 Hadoop 软件包:

cd ~
ARCH=$(arch)
if [[ "$ARCH" == "aarch64" ]]; then
    ARCH="-aarch64"
elif [[ "$ARCH" == "x86_64" ]]; then
    ARCH=""
else
    echo "$ARCH not supported"
    exit 1
fi

下载指定架构的 Hadoop 软件包:

wget https://archive.apache.org/dist/hadoop/common/hadoop-$HADOOP_VERSION/hadoop-$HADOOP_VERSION$ARCH.tar.gz
tar zxf hadoop-$HADOOP_VERSION$ARCH.tar.gz
ln -s hadoop-$HADOOP_VERSION $HOME/hadoop 
echo "export HADOOP_HOME=$HOME/hadoop" >> ~/.bashrc
echo "export PATH=$PATH:$HOME/hadoop/bin:$HOME/hadoop/sbin" >> ~/.bashrc
source  ~/.bashrc
mkdir $HADOOP_HOME/tmp
mkdir $HADOOP_HOME/hdfs
mkdir $HADOOP_HOME/hdfs/name
mkdir $HADOOP_HOME/hdfs/data
hadoop version

生成密钥用于无密码登录:

ssh-keygen -t rsa -P '' -f ~/.ssh/id_rsa
cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys
ssh localhost
exit

配置 Hadoop 环境,首先修改 Hadoop-env.sh 脚本:

IPADDR=localhost
cp $HADOOP_HOME/etc/hadoop/hadoop-env.sh $HADOOP_HOME/etc/hadoop/hadoop-env.sh.bak
echo "export JAVA_HOME=${JAVA_HOME}" >> $HADOOP_HOME/etc/hadoop/hadoop-env.sh

修改 core-site.xml 配置文件:

cp $HADOOP_HOME/etc/hadoop/core-site.xml $HADOOP_HOME/etc/hadoop/core-site.xml.bak
cat << EOF > $HADOOP_HOME/etc/hadoop/core-site.xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <property>
        <name>fs.defaultFS</name>
        <value>hdfs://$IPADDR:9000</value>
    </property>
    <property>
        <name>hadoop.tmp.dir</name>
        <value>/home/ec2-user/hadoop/tmp</value>
    </property>
    <property>
        <name>hadoop.proxyuser.hive.hosts</name>
        <value>*</value>
    </property>
    <property>
        <name>hadoop.proxyuser.hive.groups</name>
        <value>*</value>
    </property>
</configuration>
EOF

修改 hdfs-site.xml 配置文件:

cp $HADOOP_HOME/etc/hadoop/hdfs-site.xml $HADOOP_HOME/etc/hadoop/hdfs-site.xml.bak
cat << EOF > $HADOOP_HOME/etc/hadoop/hdfs-site.xml
<?xml version="1.0"?>
<configuration>
    <property>
        <name>dfs.replication</name>
        <value>1</value>
    </property>
    <property>
        <name>dfs.name.dir</name>
        <value>$HADOOP_HOME/hdfs/name</value>
    </property>
    <property>
        <name>dfs.data.dir</name>
        <value>$HADOOP_HOME/hdfs/data</value>
    </property>
</configuration>
EOF

修改 yarn-site.xml 配置文件:

cp $HADOOP_HOME/etc/hadoop/yarn-site.xml $HADOOP_HOME/etc/hadoop/yarn-site.xml.bak
cat << EOF > $HADOOP_HOME/etc/hadoop/yarn-site.xml
<?xml version="1.0"?>
<configuration>
    <property>
        <name>yarn.resourcemanager.hostname</name>
        <value>${IPADDR}</value>
    </property>
    <property>
        <name>yarn.nodemanager.aux-services</name>
        <value>mapreduce_shuffle</value>
    </property>
</configuration>
EOF

修改 mapred-site.xml 配置文件:

cp $HADOOP_HOME/etc/hadoop/mapred-site.xml $HADOOP_HOME/etc/hadoop/mapred-site.xml.bak
cat << EOF > $HADOOP_HOME/etc/hadoop/mapred-site.xml
<?xml version="1.0"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<configuration>
    <property>
        <name>mapreduce.framework.name</name>
        <value>yarn</value>
    </property>
    <property>
        <name>yarn.app.mapreduce.am.env</name>
        <value>HADOOP_MAPRED_HOME=/home/ec2-user/hadoop</value>
    </property>
    <property>
        <name>mapreduce.map.env</name>
        <value>HADOOP_MAPRED_HOME=/home/ec2-user/hadoop</value>
    </property>
    <property>
        <name>mapreduce.reduce.env</name>
        <value>HADOOP_MAPRED_HOME=/home/ec2-user/hadoop</value>
    </property>
</configuration>
EOF

初始化 HDFS 节点,启动 DFS 和 Yarn:

$HADOOP_HOME/bin/hdfs namenode -format
$HADOOP_HOME/sbin/start-dfs.sh
grep clusterID $(find $HADOOP_HOME/ -name VERSION)
$HADOOP_HOME/sbin/start-yarn.sh
jps

安装和配置 Hive 软件

下载和安装 Hive 软件:

cd ~
wget https://dlcdn.apache.org/hive/hive-$HIVE_VERSION/apache-hive-$HIVE_VERSION-bin.tar.gz
tar zxf apache-hive-$HIVE_VERSION-bin.tar.gz
ln -s $HOME/apache-hive-$HIVE_VERSION-bin hive
echo "export HIVE_HOME=$HOME/hive" >> ~/.bashrc
echo "export PATH=$PATH:$HOME/hive/bin:$HOME/hive/sbin" >> ~/.bashrc
source  ~/.bashrc

修改 hive-site.xml  配置文件:

cp $HIVE_HOME/conf/hive-default.xml.template $HIVE_HOME/conf/hive-site.xml
sed -i "s/system:java.io.tmpdir/java.io.tmpdir/g" $HIVE_HOME/conf/hive-site.xml
sed -i "s/system:user.name/user.name/g" $HIVE_HOME/conf/hive-site.xml

在配置 hive-site.xml 中,有一部分配置项不便通过 Shell 脚本自动完成,请通过下面指导手工完成。

vi $HIVE_HOME/conf/hive-site.xml

请按照下表中 <name> 查找到对应的对应的 <property> 配置项,再对 <value> 值进行修改适配:

<name> <value>
hive.exec.scratchdir /home/ec2-user/hive/tmp
hive.metastore.warehouse.dir /home/ec2-user/hive/warehouse
hive.querylog.location /home/ec2-user/hive/log
javax.jdo.option.ConnectionURL jdbc:mysql://localhost:3306/hive?createDatabaseIfNotExist=true&amp;characterEncoding=UTF-8&amp;useSSL=false&amp;allowPublicKeyRetrieval=true
javax.jdo.option.ConnectionDriverName com.mysql.jdbc.Driver
javax.jdo.option.ConnectionUserName hive
javax.jdo.option.ConnectionPassword Hive313Mysql
datanucleus.schema.autoCreateAll true
hive.metastore.schema.verification false
hive.server2.enable.doAs false
hive.server2.thrift.bind.host localhost

同时,删除文件/home/ec2-user/apache-hive-3.1.3-bin/conf/hive-site.xml 中 3215 行的特殊字符【&#8;】。

配置 MySQL Connector

cd ~
wget https://downloads.mysql.com/archives/get/p/3/file/mysql-connector-java-5.1.49.tar.gz
tar zxf mysql-connector-java-5.1.49.tar.gz
cp mysql-connector-java-5.1.49/mysql-connector-java-5.1.49.jar $HIVE_HOME/lib/
sudo ln -s $HIVE_HOME/lib/mysql-connector-java-5.1.49.jar /usr/share/java/mysql-connector-java.jar
$HIVE_HOME/bin/schematool -dbType mysql -initSchema

启动 Hive 服务

nohup hive --service metastore &
nohup hive --service hiveserver2 &

可通过 jps 查看启动的进程,并通过 hive 命令查看目前存在的数据库:

jps
hive -e "show databases;"

验证结果如下图所示:

安装和配置 Spark 软件

安装 Spark 软件:

cd ~
wget https://archive.apache.org/dist/spark/spark-$SPARK_VERSION/spark-$SPARK_VERSION-bin-hadoop3.tgz
tar zxf spark-$SPARK_VERSION-bin-hadoop3.tgz
ln -s spark-$SPARK_VERSION-bin-hadoop3 spark
echo "export SPARK_HOME=$HOME/spark" >> ~/.bashrc
echo "export PATH=$PATH:$HOME/spark/bin:$HOME/spark/sbin" >> ~/.bashrc
source ~/.bashrc

配置 Spark 软件:

cp $HIVE_HOME/conf/hive-site.xml $SPARK_HOME/conf/
cp $HADOOP_HOME/etc/hadoop/core-site.xml $HADOOP_HOME/etc/hadoop/hdfs-site.xml $SPARK_HOME/conf/
cp $SPARK_HOME/conf/log4j2.properties.template $SPARK_HOME/conf/log4j2.properties
sed -i "s/rootLogger.level = info/rootLogger.level = error/g" $SPARK_HOME/conf/log4j2.properties
ln -s /usr/share/java/mysql-connector-java.jar $SPARK_HOME/jars/mysql-connector-java.jar

启动 Spark Local 集群

在成功完成前面各组件的安装配置后,通过下面命令启动 Spark Local 集群:

$SPARK_HOME/sbin/start-all.sh

通过下面命令进行初步验证,spark-example 将完成指定位数的 Pi 值计算。

spark-sql -e "show databases;"
spark-submit --class org.apache.spark.examples.SparkPi \
$SPARK_HOME/examples/jars/spark-examples_2.12-3.3.1.jar 100

在 Spark 成功启动之后,可以得到如下验证结果:

安装和配置 Benchmark 工具

cd ~
git clone https://github.com/hortonworks/hive-testbench.git
cd $HOME/hive-testbench
./tpcds-build.sh

在 TPC-DS Benchmark 工具邮件完成之后,界面如下提示:

配置 benchmark 工具:

IPADDR=localhost
sed -i "s/localhost:2181\/;serviceDiscoveryMode=zooKeeper;zooKeeperNamespace=hiveserver2?tez.queue.name=default/${IPADDR}:10000\//" $HOME/hive-testbench/tpcds-setup.sh
sed -i "s/hive.optimize.sort.dynamic.partition.threshold=0/hive.optimize.sort.dynamic.partition=true/" $HOME/hive-testbench/settings/*.sql

生成测试数据集

通过指定 SF 的值,设置程序需要生成的数据量,本文中 SF=100 表示生成 100GB 的数据量。根据生成的数据量大小差异,此过程可能会持续数分钟到数小时不等。

cd $HOME/hive-testbench
SF=100
./tpcds-setup.sh $SF

生成数据过程如下图所示:

数据生成步骤完成后,如下图所示:

执行全部的 SQL 分析任务

待数据全部完成之后,预先准备 Benchmark 过程中需要的一些结果目录:

cd ~
SUT_NAME="spark-tpcds"
PN=$(sudo dmidecode -s system-product-name | tr ' ' '_')
DATA_DIR=~/${PN}_${SUT_NAME}
CFG_DIR=$DATA_DIR/system-infomation
TPCDS_RESULT_DIR=$DATA_DIR/spark-tpcds-result
LOG_DIR=$DATA_DIR/logs
mkdir -p $DATA_DIR $CFG_DIR $TPCDS_RESULT_DIR $LOG_DIR
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
RESULT_PATH="$TPCDS_RESULT_DIR/$TIMESTAMP"
mkdir -p $RESULT_PATH
RESULT_SUMMARY="$RESULT_PATH/result_summary_spark_tpc-ds.txt"

采用逐个执行 *.sql 文件的方式执行 Benchmark:

echo "[$TIMESTAMP] Start Spark TPC-DS Benchmark(SF=$SF) on $PN ...... " > $RESULT_SUMMARY
cd $HOME/hive-testbench/spark-queries-tpcds
LIST=$(ls *.sql)
for i in $LIST; do
    RESULT_LOG="$RESULT_PATH/$i.log"
    RESULT_OUT="$RESULT_PATH/$i.out"
    spark-sql --driver-memory 4G --database tpcds_bin_partitioned_orc_$SF -f $i \ 1>$RESULT_OUT 2>$RESULT_LOG
    execution_time=$(grep "Time taken" $RESULT_LOG)
    echo "[$(date +%Y%m%d-%H%M%S)] $i : $execution_time " >> $RESULT_SUMMARY
done

查看 Benchmark 执行结果

当每一个 SQL 文件在执行时,可以通过下面步骤查看执行进程:

cd $RESULT_PATH

在这个目录中,有 3 类文件:

  • *.log:表示某一个 SQL 文件执行信息,主要记录了该 SQL 文件执行完成的时间;
  • *.out :表示某一个 SQL 文件在执行完成后,所返回的记录;
  • result_summary_spark_tpc-ds.txt :记录了本次 Benchmark 执行过程中所有的 SQL 文件的执行时间。

你可以通过 tail/more/cat 等命令查看 Benchmark 执行结果,如下图所示:

在所有 SQL 文件都完成之后,您可以将 result_summary_spark_tpc-ds.txt 下载到本地电脑,通过 Excel 等工具对数据进行处理,例如计算所有 SQL 文件的总完成时间。

查看 Benchmark 执行时的监控信息

在 Benchmark 执行时,你可以这台 EC2 实例上,通过 top/htop 等监控命令查看 CPU/内存利用率等信息。

在 Benchmark 结束后,可以 在 EC2 控制台查看 EC2 实例的 CPU 利用率等监控指标。

本篇作者

袁泉

亚马逊云科技弹性计算解决方案架构师,主要负责亚马逊云科技弹性计算相关产品的技术咨询与方案设计。专注于弹性计算相关的产品和方向。