sidebar_label | sidebar_position | slug |
---|---|---|
Hadoop 使用 JuiceFS |
3 |
/hadoop_java_sdk |
JuiceFS 提供与 HDFS 接口高度兼容的 Java 客户端,Hadoop 生态中的各种应用都可以在不改变代码的情况下,平滑地使用 JuiceFS 存储数据。
JuiceFS Hadoop Java SDK 同时兼容 Hadoop 2.x、Hadoop 3.x,以及 Hadoop 生态中的各种主流组件。
JuiceFS 默认使用本地的 用户
和 UID
映射,在分布式环境下使用时,为了避免权限问题,请参考文档将需要使用的 用户
和 UID
同步到所有 Hadoop 节点。也可以通过定义一个全局的用户和用户组文件给集群共享读取,查看详情。
通过 JuiceFS Java 客户端为 Hadoop 生态提供存储,需要提前创建 JuiceFS 文件系统。部署 Java 客户端时,在配置文件中指定已创建文件系统的元数据引擎地址。
创建文件系统可以参考 JuiceFS 快速上手指南。
:::note 注意 如果要在分布式环境中使用 JuiceFS,创建文件系统时,请合理规划要使用的对象存储和数据库,确保它们可以被每个集群节点正常访问。 :::
JuiceFS Hadoop Java SDK 最多需要额外使用 4 * juicefs.memory-size
的 off-heap 内存用来加速读写性能,默认情况下,最多需要额外 1.2GB 内存(取决于写入负载)。
请参考「安装与升级」文档了解如何下载预编译的 JuiceFS Hadoop Java SDK。
:::note 注意 不论为哪个系统环境编译客户端,编译后的 JAR 文件都为相同的名称,且只能部署在匹配的系统环境中,例如在 Linux 中编译则只能用于 Linux 环境。另外,由于编译的包依赖 glibc,建议尽量使用低版本的系统进行编译,这样可以获得更好的兼容性。 :::
编译依赖以下工具:
克隆仓库:
$ git clone https://github.com/juicedata/juicefs.git
进入目录,执行编译:
:::note 注意
如果使用 Ceph 的 RADOS 作为 JuiceFS 的存储引擎,需要先安装 librados-dev
包并且在编译 libjfs.so
时加上 -tags ceph
。
:::
$ cd juicefs/sdk/java
$ make
编译完成后,可以在 sdk/java/target
目录中找到编译好的 JAR
文件,包括两个版本:
juicefs-hadoop-X.Y.Z.jar
original-juicefs-hadoop-X.Y.Z.jar
建议使用包含第三方依赖的版本。
用于 Windows 环境的客户端需要在 Linux 或 macOS 系统上通过交叉编译的方式获得,编译依赖 mingw-w64,需要提前安装。
与编译面向 Linux 和 macOS 客户端的步骤相同,比如在 Ubuntu 系统上,先安装 mingw-w64
包,解决依赖问题:
$ sudo apt install mingw-w64
克隆并进入 JuiceFS 源代码目录,执行以下代码进行编译:
$ cd juicefs/sdk/java
$ make win
让 Hadoop 生态各组件能够正确识别 JuiceFS,需要进行以下配置:
$JAVA_HOME/lib/tools.jar
放置到组件的 classpath
内,常见大数据平台和组件的安装路径见下表。core-site.xml
),详见客户端配置参数。建议将 JAR 文件放置在一个统一的位置,其他位置通过符号链接进行调用。
名称 | 安装路径 |
---|---|
CDH |
/opt/cloudera/parcels/CDH/lib/hadoop/lib /opt/cloudera/parcels/CDH/spark/jars /var/lib/impala
|
HDP |
/usr/hdp/current/hadoop-client/lib /usr/hdp/current/hive-client/auxlib /usr/hdp/current/spark2-client/jars
|
Amazon EMR |
/usr/lib/hadoop/lib /usr/lib/spark/jars /usr/lib/hive/auxlib
|
阿里云 EMR |
/opt/apps/ecm/service/hadoop/*/package/hadoop*/share/hadoop/common/lib /opt/apps/ecm/service/spark/*/package/spark*/jars /opt/apps/ecm/service/presto/*/package/presto*/plugin/hive-hadoop2 /opt/apps/ecm/service/hive/*/package/apache-hive*/lib /opt/apps/ecm/service/impala/*/package/impala*/lib
|
腾讯云 EMR |
/usr/local/service/hadoop/share/hadoop/common/lib /usr/local/service/presto/plugin/hive-hadoop2 /usr/local/service/spark/jars /usr/local/service/hive/auxlib
|
UCloud UHadoop |
/home/hadoop/share/hadoop/common/lib /home/hadoop/hive/auxlib /home/hadoop/spark/jars /home/hadoop/presto/plugin/hive-hadoop2
|
百度云 EMR |
/opt/bmr/hadoop/share/hadoop/common/lib /opt/bmr/hive/auxlib /opt/bmr/spark2/jars
|
名称 | 安装路径 |
---|---|
Spark | ${SPARK_HOME}/jars |
Presto | ${PRESTO_HOME}/plugin/hive-hadoop2 |
Flink | ${FLINK_HOME}/lib |
请参考以下表格设置 JuiceFS 文件系统相关参数,并写入配置文件,一般是 core-site.xml
。
配置项 | 默认值 | 描述 |
---|---|---|
fs.jfs.impl |
io.juicefs.JuiceFileSystem |
指定要使用的存储实现,默认使用 jfs:// 作为 scheme。如想要使用其它 scheme(例如 cfs:// ),则修改为 fs.cfs.impl 即可。无论使用的 scheme 是什么,访问的都是 JuiceFS 中的数据。 |
fs.AbstractFileSystem.jfs.impl |
io.juicefs.JuiceFS |
指定要使用的存储实现,默认使用 jfs:// 作为 scheme。如想要使用其它 scheme(例如 cfs:// ),则修改为 fs.AbstractFileSystem.cfs.impl 即可。无论使用的 scheme 是什么,访问的都是 JuiceFS 中的数据。 |
juicefs.meta |
指定预先创建好的 JuiceFS 文件系统的元数据引擎地址。可以通过 juicefs.{vol_name}.meta 格式为客户端同时配置多个文件系统。具体请参考「多文件系统配置」。 |
配置项 | 默认值 | 描述 |
---|---|---|
juicefs.cache-dir |
设置本地缓存目录,可以指定多个文件夹,用冒号 : 分隔,也可以使用通配符(比如 * )。请预先创建好这些目录,并给予 0777 权限,便于多个应用共享缓存数据。
|
|
juicefs.cache-size |
0 | 设置本地缓存目录的容量,单位 MiB,默认为 0,即不开启缓存。如果配置了多个缓存目录,该值代表所有缓存目录容量的总和。 |
juicefs.cache-full-block |
true |
是否缓存所有读取的数据块,false 表示只缓存随机读的数据块。 |
juicefs.free-space |
0.1 | 本地缓存目录的最小可用空间比例,默认保留 10% 剩余空间。 |
juicefs.attr-cache |
0 | 目录和文件属性缓存的过期时间(单位:秒) |
juicefs.entry-cache |
0 | 文件项缓存的过期时间(单位:秒) |
juicefs.dir-entry-cache |
0 | 目录项缓存的过期时间(单位:秒) |
juicefs.discover-nodes-url |
指定发现集群节点列表的方式,每 10 分钟刷新一次。 YARN: yarn Spark Standalone: http://spark-master:web-ui-port/json/ Spark ThriftServer: http://thrift-server:4040/api/v1/applications/ Presto: http://coordinator:discovery-uri-port/v1/service/presto/
|
配置项 | 默认值 | 描述 |
---|---|---|
juicefs.max-uploads |
20 | 上传数据的最大连接数 |
juicefs.max-deletes |
2 | 删除数据的最大连接数 |
juicefs.get-timeout |
5 | 下载一个对象的超时时间,单位为秒。 |
juicefs.put-timeout |
60 | 上传一个对象的超时时间,单位为秒。 |
juicefs.memory-size |
300 | 读写数据的缓冲区最大空间,单位为 MiB。 |
juicefs.prefetch |
1 | 预读数据块的线程数 |
juicefs.upload-limit |
0 | 上传带宽限制,单位为 Mbps,默认不限制。 |
juicefs.download-limit |
0 | 下载带宽限制,单位为 Mbps,默认不限制。 |
配置项 | 默认值 | 描述 |
---|---|---|
juicefs.bucket |
为对象存储指定跟格式化时不同的访问地址 | |
juicefs.debug |
false |
是否开启 debug 日志 |
juicefs.access-log |
访问日志的路径。需要所有应用都有写权限,可以配置为 /tmp/juicefs.access.log 。该文件会自动轮转,保留最近 7 个文件。 |
|
juicefs.superuser |
hdfs |
超级用户 |
juicefs.users |
null |
用户名以及 UID 列表文件的地址,比如 jfs://name/etc/users 。文件格式为 <username>:<UID> ,一行一个用户。 |
juicefs.groups |
null |
用户组、GID 以及组成员列表文件的地址,比如 jfs://name/etc/groups 。文件格式为 <group-name>:<GID>:<username1>,<username2> ,一行一个用户组。 |
juicefs.umask |
null |
创建文件和目录的 umask 值(如 0022 ),如果没有此配置,默认值是 fs.permissions.umask-mode 。 |
juicefs.push-gateway |
Prometheus Pushgateway 地址,格式为 <host>:<port> 。 |
|
juicefs.push-auth |
Prometheus 基本认证信息,格式为 <username>:<password> 。 |
|
juicefs.push-graphite |
Graphite 地址,格式为 <host>:<port> 。 |
|
juicefs.push-interval |
10 | 指标推送的时间间隔,单位为秒。 |
juicefs.fast-resolve |
true |
是否开启快速元数据查找(通过 Redis Lua 脚本实现) |
juicefs.no-usage-report |
false |
是否上报数据。仅上版本号等使用量数据,不包含任何用户信息。 |
juicefs.block.size |
134217728 |
单位为字节,同 HDFS 的 dfs.blocksize ,默认 128 MB |
juicefs.file.checksum |
false |
DistCp 使用 -update 参数时,是否计算文件 Checksum |
当需要同时使用多个 JuiceFS 文件系统时,上述所有配置项均可对特定文件系统进行指定,只需要将文件系统名字放在配置项的中间,比如下面示例中的 jfs1
和 jfs2
:
<property>
<name>juicefs.jfs1.meta</name>
<value>redis://jfs1.host:port/1</value>
</property>
<property>
<name>juicefs.jfs2.meta</name>
<value>redis://jfs2.host:port/1</value>
</property>
以下是一个常用的配置示例,请替换 juicefs.meta
配置中的 {HOST}
、{PORT}
和 {DB}
变量为实际的值。
<property>
<name>fs.jfs.impl</name>
<value>io.juicefs.JuiceFileSystem</value>
</property>
<property>
<name>fs.AbstractFileSystem.jfs.impl</name>
<value>io.juicefs.JuiceFS</value>
</property>
<property>
<name>juicefs.meta</name>
<value>redis://{HOST}:{PORT}/{DB}</value>
</property>
<property>
<name>juicefs.cache-dir</name>
<value>/data*/jfs</value>
</property>
<property>
<name>juicefs.cache-size</name>
<value>1024</value>
</property>
<property>
<name>juicefs.access-log</name>
<value>/tmp/juicefs.access.log</value>
</property>
请参照前述各项配置表,将配置参数加入到 Hadoop 配置文件 core-site.xml
中。
如果使用的是 CDH 6 版本,除了修改 core-site
外,还需要通过 YARN 服务界面修改 mapreduce.application.classpath
,增加:
$HADOOP_COMMON_HOME/lib/juicefs-hadoop.jar
除了修改 core-site
外,还需要通过 MapReduce2 服务界面修改配置 mapreduce.application.classpath
,在末尾增加(变量无需替换):
/usr/hdp/${hdp.version}/hadoop/lib/juicefs-hadoop.jar
将配置参数加入 conf/flink-conf.yaml
。如果只是在 Flink 中使用 JuiceFS, 可以不在 Hadoop 环境配置 JuiceFS,只需要配置 Flink 客户端即可。
:::note 注意 Hudi 自 v0.10.0 版本开始支持 JuiceFS,请确保使用正确的版本。 :::
请参考「Hudi 官方文档」了解如何配置 JuiceFS。
可以使用 Kafka Connect 和 HDFS Sink Connector(HDFS 2, HDFS 3)将数据落盘存储到 JuiceFS。
首先需要将 JuiceFS 的 SDK 添加到 Kafka Connect 的 classpath
内,如 /usr/share/java/confluentinc-kafka-connect-hdfs/lib
.
在新建 Connect Sink 任务时,做如下配置:
hadoop.conf.dir
为包含 core-site.xml
配置文件的目录,若没有运行在 Hadoop 环境,可创建一个单独目录,如 /usr/local/juicefs/hadoop
,然后将与 JuiceFS 相关的配置添加到 core-site.xml
store.url
为 jfs://
的路径举例:
# 省略其他配置项...
hadoop.conf.dir=/path/to/hadoop-conf
store.url=jfs://path/to/store
当需要使用以下组件访问 JuiceFS 数据时,需要重启相关服务。
:::note 注意
在重启之前需要保证 JuiceFS 配置已经写入配置文件,通常可以查看机器上各组件配置的 core-site.xml
里面是否有 JuiceFS 相关配置。
:::
组件名 | 服务名 |
---|---|
Hive | HiveServer Metastore |
Spark | ThriftServer |
Presto | Coordinator Worker |
Impala | Catalog Server Daemon |
HBase | Master RegionServer |
HDFS、Hue、ZooKeeper 等服务无需重启。
若访问 JuiceFS 出现 Class io.juicefs.JuiceFileSystem not found
或 No FilesSystem for scheme: jfs
错误,请参考 FAQ。
JuiceFS Java 客户端部署完成以后,可以采用以下方式验证部署是否成功。
$ hadoop fs -ls jfs://{JFS_NAME}/
:::info 说明
这里的 JFS_NAME
是创建 JuiceFS 文件系统时指定的名称。
:::
CREATE TABLE IF NOT EXISTS person
(
name STRING,
age INT
) LOCATION 'jfs://{JFS_NAME}/tmp/person';
请查看「监控」文档了解如何收集及展示 JuiceFS 监控指标
从 HDFS 迁移数据到 JuiceFS,一般是使用 DistCp 来拷贝数据,它支持数据校验 (Checksum) 来保证数据的正确性。
DistCp 是使用 HDFS 的 getFileChecksum()
接口来获得文件的校验码,然后对比拷贝后的文件的校验码来确保数据是一样的。
Hadoop 默认使用的 Checksum 算法是 MD5-MD5-CRC32, 严重依赖 HDFS 的实现细节。它是根据文件目前的分块形式,使用 MD5-CRC32 算法汇总每一个数据块的 Checksum(把每一个 64K 的 block 的 CRC32 校验码汇总,再算一个 MD5),然后再用 MD5 计算校验码。如果 HDFS 集群的分块大小不同,就没法用这个算法进行比较。
为了兼容 HDFS,JuiceFS 也实现了该 MD5-MD5-CRC32 算法,它会将文件的数据读一遍,用同样的算法计算得到一个 checksum,用于比较。
因为 JuiceFS 是基于对象存储实现的,后者已经通过多种 Checksum 机制保证了数据完整性,JuiceFS 默认没有启用上面的 Checksum 算法,需要通过 juicefs.file.checksum
配置来启用。
因为该算法依赖于相同的分块大小,需要通过 juicefs.block.size
配置将分块大小设置为跟 HDFS 一样(默认值是 dfs.blocksize
,它的默认值是 128MB)。
另外,HDFS 里支持给每一个文件设置不同的分块大小,而 JuiceFS 不支持,如果启用 Checksum 校验的话会导致拷贝部分文件失败(因为分块大小不同),JuiceFS Hadoop Java SDK 对 DistCp 打了一个热补丁(需要 tools.jar)来跳过这些分块不同的文件(不做比较,而不是抛异常)。
以下提供了一系列方法,使用 JuiceFS 客户端内置的压测工具,对已经成功部署了客户端环境进行性能测试。
create
hadoop jar juicefs-hadoop.jar nnbench create -files 10000 -baseDir jfs://{JFS_NAME}/tmp/benchmarks/NNBench -local
此命令会 create 10000 个空文件
open
hadoop jar juicefs-hadoop.jar nnbench open -files 10000 -baseDir jfs://{JFS_NAME}/tmp/benchmarks/NNBench -local
此命令会 open 10000 个文件,并不读取数据
rename
hadoop jar juicefs-hadoop.jar nnbench rename -files 10000 -baseDir jfs://{JFS_NAME}/tmp/benchmarks/NNBench -local
delete
hadoop jar juicefs-hadoop.jar nnbench delete -files 10000 -baseDir jfs://{JFS_NAME}/tmp/benchmarks/NNBench -local
参考值
操作 | TPS | 时延(ms) |
---|---|---|
create | 644 | 1.55 |
open | 3467 | 0.29 |
rename | 483 | 2.07 |
delete | 506 | 1.97 |
顺序写
hadoop jar juicefs-hadoop.jar dfsio -write -size 20000 -baseDir jfs://{JFS_NAME}/tmp/benchmarks/DFSIO -local
顺序读
hadoop jar juicefs-hadoop.jar dfsio -read -size 20000 -baseDir jfs://{JFS_NAME}/tmp/benchmarks/DFSIO -local
如果多次运行此命令,可能会出现数据被缓存到了系统缓存而导致读取速度非常快,只需清除 JuiceFS 的本地磁盘缓存即可
参考值
操作 | 吞吐(MB/s) |
---|---|
write | 647 |
read | 111 |
如果机器的网络带宽比较低,则一般能达到网络带宽瓶颈
以下命令会启动 MapReduce 分布式任务程序对元数据和 IO 性能进行测试,测试时需要保证集群有足够的资源能够同时启动所需的 map 任务。
本项测试使用的计算资源:
create
hadoop jar juicefs-hadoop.jar nnbench create -maps 10 -threads 10 -files 1000 -baseDir jfs://{JFS_NAME}/tmp/benchmarks/NNBench
此命令会启动 10 个 map task,每个 task 有 10 个线程,每个线程会创建 1000 个空文件,总共 100000 个空文件
open
hadoop jar juicefs-hadoop.jar nnbench open -maps 10 -threads 10 -files 1000 -baseDir jfs://{JFS_NAME}/tmp/benchmarks/NNBench
此命令会启动 10 个 map task,每个 task 有 10 个线程,每个线程会 open 1000 个文件,总共 open 100000 个文件
rename
hadoop jar juicefs-hadoop.jar nnbench rename -maps 10 -threads 10 -files 1000 -baseDir jfs://{JFS_NAME}/tmp/benchmarks/NNBench
此命令会启动 10 个 map task,每个 task 有 10 个线程,每个线程会 rename 1000 个文件,总共 rename 100000 个文件
delete
hadoop jar juicefs-hadoop.jar nnbench delete -maps 10 -threads 10 -files 1000 -baseDir jfs://{JFS_NAME}/tmp/benchmarks/NNBench
此命令会启动 10 个 map task,每个 task 有 10 个线程,每个线程会 delete 1000 个文件,总共 delete 100000 个文件
参考值
10 并发
操作 | IOPS | 时延(ms) |
---|---|---|
create | 4178 | 2.2 |
open | 9407 | 0.8 |
rename | 3197 | 2.9 |
delete | 3060 | 3.0 |
100 并发
操作 | IOPS | 时延(ms) |
---|---|---|
create | 11773 | 7.9 |
open | 34083 | 2.4 |
rename | 8995 | 10.8 |
delete | 7191 | 13.6 |
连续写
hadoop jar juicefs-hadoop.jar dfsio -write -maps 10 -size 10000 -baseDir jfs://{JFS_NAME}/tmp/benchmarks/DFSIO
此命令会启动 10 个 map task,每个 task 写入 10000MB 的数据
连续读
hadoop jar juicefs-hadoop.jar dfsio -read -maps 10 -size 10000 -baseDir jfs://{JFS_NAME}/tmp/benchmarks/DFSIO
此命令会启动 10 个 map task,每个 task 读取 10000MB 的数据
参考值
操作 | 平均吞吐(MB/s) | 总吞吐(MB/s) |
---|---|---|
write | 198 | 1835 |
read | 124 | 1234 |
测试数据集 100GB 规模,测试 Parquet 和 ORC 两种文件格式。
本次测试仅测试前 10 个查询。
使用 Spark Thrift JDBC/ODBC Server 开启 Spark 常驻进程,然后通过 Beeline 连接提交任务。
节点类型 | 机器型号 | CPU | 内存 | 磁盘 | 数量 |
---|---|---|---|---|---|
Master | 阿里云 ecs.r6.xlarge | 4 | 32GiB | 系统盘:100GiB | 1 |
Core | 阿里云 ecs.r6.xlarge | 4 | 32GiB | 系统盘:100GiB 数据盘:500GiB 高效云盘 x 2 |
3 |
${SPARK_HOME}/sbin/start-thriftserver.sh \
--master yarn \
--driver-memory 8g \
--executor-memory 10g \
--executor-cores 3 \
--num-executors 3 \
--conf spark.locality.wait=100 \
--conf spark.sql.crossJoin.enabled=true \
--hiveconf hive.server2.thrift.port=10001
Core 节点的 2 块数据盘挂载在 /data01
和 /data02
目录下,core-site.xml
配置如下:
<property>
<name>juicefs.cache-size</name>
<value>200000</value>
</property>
<property>
<name>juicefs.cache-dir</name>
<value>/data*/jfscache</value>
</property>
<property>
<name>juicefs.cache-full-block</name>
<value>false</value>
</property>
<property>
<name>juicefs.discover-nodes-url</name>
<value>yarn</value>
</property>
<property>
<name>juicefs.attr-cache</name>
<value>3</value>
</property>
<property>
<name>juicefs.entry-cache</name>
<value>3</value>
</property>
<property>
<name>juicefs.dir-entry-cache</name>
<value>3</value>
</property>
任务提交的命令如下:
${SPARK_HOME}/bin/beeline -u jdbc:hive2://localhost:10001/${DATABASE} \
-n hadoop \
-f query{i}.sql
JuiceFS 可以使用本地磁盘作为缓存加速数据访问,以下数据是分别使用 Redis 和 TiKV 作为 JuiceFS 的元数据引擎跑 4 次后的结果(单位秒)。
Queries | JuiceFS (Redis) | JuiceFS (TiKV) | HDFS |
---|---|---|---|
q1 | 20 | 20 | 20 |
q2 | 28 | 33 | 26 |
q3 | 24 | 27 | 28 |
q4 | 300 | 309 | 290 |
q5 | 116 | 117 | 91 |
q6 | 37 | 42 | 41 |
q7 | 24 | 28 | 23 |
q8 | 13 | 15 | 16 |
q9 | 87 | 112 | 89 |
q10 | 23 | 24 | 22 |
Queries | JuiceFS (Redis) | JuiceFS (TiKV) | HDFS |
---|---|---|---|
q1 | 33 | 35 | 39 |
q2 | 28 | 32 | 31 |
q3 | 23 | 25 | 24 |
q4 | 273 | 284 | 266 |
q5 | 96 | 107 | 94 |
q6 | 36 | 35 | 42 |
q7 | 28 | 30 | 24 |
q8 | 11 | 12 | 14 |
q9 | 85 | 97 | 77 |
q10 | 24 | 28 | 38 |
Class io.juicefs.JuiceFileSystem not found
异常出现这个异常的原因是 juicefs-hadoop.jar 没有被加载,可以用 lsof -p {pid} | grep juicefs
查看 JAR 文件是否被加载。需要检查 JAR 文件是否被正确地放置在各个组件的 classpath 里面,并且保证 JAR 文件有可读权限。
另外,在某些发行版 Hadoop 环境中,需要修改 mapred-site.xml
中的 mapreduce.application.classpath
参数,添加 juicefs-hadoop.jar 的路径。
No FilesSystem for scheme: jfs
异常出现这个异常的原因是 core-site.xml
配置文件中的 JuiceFS 配置没有被读取到,需要检查组件配置的 core-site.xml
中是否有 JuiceFS 相关配置。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。