Hadoop 3.x 时代,EC 露个脸呗!_HDFS

在本文中,笔者将详细研究 HDFS EC(Erasure Coding,纠删码),讲解什么是 HDFS EC,引入 EC 的原因,用于 EC 的算法,以及在 Hadoop 社区中为 EC 做的架构优化,最后再结合 Hive 实战 HDFS EC。

HDFS 复制算法的问题

为了提供容错,HDFS 根据复制因子的大小在不同的 DataNodes 节点复制文件块数据。复制因子默认值为 3,则表明原始数据块有多余的两个副本,这些副本也消耗其他资源,例如网络带宽和存储。

Hadoop 3.x 时代,EC 露个脸呗!_hive_02

在 N 个复制因子的复制中,存在 N-1 个容错能力,存储效率为 1/N。所以这种方式的复制增加了存储的开销和资源的浪费。对于 I/O 活动较低且很少访问的 cold 数据集和 warm 数据集的副本使用与原始数据集相同的资源。

为了解决这些问题,Hadoop 社区使用 HDFS Erasure Coding 代替默认的复制方式,以提供相同级别的容错能力,并且存储开销不超过 50%。

Erasure Coding 文件的复制因子始终为 1,无法对其进行更改。

HDFS Erasure Coding

在 Hadoop 3 中,Erasure Coding 是默认复制方式的解决方案。

Erasure Coding 本身是一种编码容错技术,其实在存储生态系统中,RAID 就使用了 Erasure Coding。RAID 通过 striping 实现 Erasure Coding,即将逻辑顺序的数据(例如文件)划分为较小的单元(bit、byte 或 block),并将连续的单元存储在不同的磁盘上

对于原始数据集的每个 strip,都会根据 Erasure Coding 算法来计算并存储一定数量的 parity cells,该过程称为编码。

可以基于剩余存活的数据和 parity cells 从计算中恢复任何 striping cell 中的错误,该过程称为解码。

因此,在 HDFS 中使用 Erasure Coding 可提高存储效率,同时提供与传统的基于复制的 HDFS 部署相同水平的容错能力和数据持久性。

例如:一个具有 6 个 blocks 的文件,复制因子为 3,将占用磁盘空间的 18 个 blocks。

Hadoop 3.x 时代,EC 露个脸呗!_hdfs_03

如果使用 Erasure Coding,那么一个具有 6 个 blocks 的文件将仅占用磁盘空间的 9 个 blocks(6 data,3 parity)。

Hadoop 3.x 时代,EC 露个脸呗!_hdfs_04

HDFS Erasure Coding 算法

HDFS Erasure Coding 使用 EC 算法来计算每个数据 block cell 的 parity。 

Erasure Coding 的支持不同的算法。

1. XOR 算法

它是一种基于 XOR(异或)运算的算法,提供了最简单的 EC 形式。XOR 操作是关联的,可从任意数量的数据 bits 中生成 1 个奇偶校验位(parity bit)。

例如,如果我们有 3 个数据单元,则使用 XOR 运算,我们可以计算奇偶校验位,比如 1 ⊕ 0 ⊕ 1 ⊕ 1 = 1。

如果任何数据位丢失,则通过对其余位和奇偶校验位进行异或来恢复。

XOR 算法对于任何数量的数据单元输入仅生成 1 个奇偶校验位,因此它只能容忍 1 个故障。

因此,对于像 HDFS 这样需要处理多个故障的系统来说,这是不够的。

XOR 算法中的容错能力为 1,存储效率为 75%。

2. Reed-Solomon Algorithm

Reed-Solomon(RS) 算法克服了 XOR 算法的限制,使用线性代数运算生成多个 parity cells,以便能够容忍多个失败。

RS 算法使用生成矩阵(GT,Generator Matrix)与 m 个数据单元相乘,以获得具有 m 个数据单元(data cells)和 n 个奇偶校验单元(parity cells)的 extended codewords。

如果存储失败,则只要 m+n cells 中的 m 个可用,就可以通过将生成器矩阵来恢复存储。

RS 中的容错能力最高为 n,即奇偶校验单元的数量和存储效率为 m / m + n,其中 m 为数据单元(data cells),n 为奇偶校验单元(parity cells)。

Hadoop Erasure Coding 架构

在 EC 的背景下,striping 具有几个关键优势:

  • 首先,它启用在在线 EC(立即以 EC 格式写入数据),避免了转换阶段并立即节省了存储空间。在线 EC 还增强了顺序 I/O 的性能。在具有高端网络的集群中,这尤其可以达到理想的性能。
  • 其次,它自然地将一个小文件分发到多个 DataNode,并且不需要将多个文件捆绑到单个 coding group 中,这极大地简化了文件操作,例如删除、配额报告以及联合名称空间之间的迁移。

为了支持 Erasure Coding,HDFS 体系架构进行了一些更改。

1. NameNode Extension

HDFS 中的文件被 striping 拆分为块组(block groups)。每个 block group 包含一定数量的内部块。为了减少这些额外块对 NameNode 内存消耗,引入了 Hierarchical block 命名的新协议。可以根据任何内部块的 ID 推导出块组 ID,允许在块组级别进行管理,而不是块级别。

2. Client Extensions

客户端的读写路径得到了增强,可以并行处理块组中的多个内部块。

在 DFSInputStream 和 DFSOutputStream 中实现的 HDFS 客户端的主要 I/O 逻辑已通过支持数据 striping 和 EC 而扩展为 DFSStripedInputStream 和 DFSStripedOutputStream。

在输出/写入路径(output/write)上,DFSStripedOutputStream 管理着一组数据 streamers,每个 DataNode 节点一个,在当前块组中存储一个内部块。streamers 通常异步工作。Coordinator 角色负责整个块组的操作,包括结束当前块组、分配新的块组等。在输入/读取路径(input/read)上,DFSStripedInputStream 将请求的逻辑字节数据范围转换为存储在 DataNodes 节点上的内部块,然后它并行发出读取请求。如果发生故障时,它将发出其他读取请求以进行解码。

3. DataNode Extensions

对于失败的 EC 块的后台恢复,DataNode 节点运行一个额外的 ErasureCodingWorker(ECWorker)。

与复制类似,NameNode 检测失败的 EC 块并选择一个 DataNode 来执行恢复任务。恢复工作作为心跳响应传递给 NameNode。

ErasureCodingWorker 执行以下三个任务来重构失败的 EC 块:

  • 使用专用线程池从源节点读取数据。根据 EC 策略,它将读请求调度到所有源目标节点,并且只读取用于恢复的输入块。
  • 解码数据并生成输出数据。
  • 将生成的数据块传输到目标节点。

4. HDFS EC 策略

HDFS 集群中的文件和目录可以具有不同的 EC 和复制策略,以适应不同的工作负载。

HDFS EC 策略结合了如何对文件进行编码和解码。每个策略都是使用以下两条信息定义的:

  1. EC schema EC schema 包括:_ 一个 EC 组中的数据块(data blocks)和奇偶校验块(parity blocks)的数量(例如,6+3) _ 编解码器算法(如 Reed-Solomon、XOR)。
  2. 一个 striping cell 大小

Hadoop Erasure Coding 工作如下:

Hadoop 3.x 时代,EC 露个脸呗!_HDFS_05

通过一个示例,让我们了解在 HDFS 中 EC 的工作原理。在 GIF 中,我们可以看到一个文件 XYZ,被分成 6 块(A1、A2、B1、B2、C1 和 C2)。

Hadoop 3.x 时代,EC 露个脸呗!_HDFS_06

使用 EC 算法计算奇偶校验块(Ap 为 A1 和 A2,Bp 为 B1 和 B2,Cp 为 C1 和 C2)。如果 A1 块丢失了,那么通过使用 A2 和奇偶校验块 Ap,A1 可以被恢复。如果 C1 块丢失了,那么通过使用 C2 和奇偶校验块 Cp,C1 可以被恢复。

HDFS EC 的优点

  • 在线 EC 支持 提供在线 EC 支持,即立即以 EC 格式写入数据,从而避免了转换阶段并节省了存储空间。此外,此在线 EC 通过并行使用多个磁盘主轴来增强顺序 I/O 性能。
  • 简化联合名称空间之间的文件操作 EC 将一个小文件分发到多个 DataNode,并且不需要将多个文件捆绑到一个编码组中。这简化了文件操作,例如配额报告、删除以及联合名称空间之间的迁移。
  • 低开销 EC 的存储开销只有 50%。
  • 双向恢复 HDFS 块错误不仅可以在读取路径时发现并恢复,而且我们可以在后台主动进行检查。
  • 兼容性 HDFS 用户可以轻松使用高级和基本功能,包括对 EC 数据进行快照、加密、追加、缓存等。

HDFS EC 的限制

  • 由于重大技术挑战,EC 文件不支持某些 HDFS 文件写入操作,例如 hflush、hsync 和 append。
  • EC 对集群在 CPU 和网络方面提出了额外的要求。
  • 由于执行远程读取,EC 在数据重建中增加了额外的开销。

基于 Hive 实战 EC

通过在 Hive 中使用 EC,可以显著提高 HDFS 的存储效率,同时继续提供数据稳定高可用性。EC 默认使用的策略为 RS-6-3-1024k,即文件被分割成 6 个数据单元(data cells)和 3 个奇偶校验单元(parity cells),总共占用 9 个磁盘数据块。

虽然 EC 对 Hive 查询来说,没有显著的性能变化,但是以下几点还是需要注意的:

  • Hive scratch 目录继续使用副本复制模式
  • 经常访问的数据继续使用副本复制模式
  • EC 需要集群环境拥有足够高的带宽
  • 对于 EC 文件,小文件问题更为严重。要解决这个问题,查看后面的实战部分内容。

Hive 何时使用 EC

官方建议,最好在不经常访问的 Hive 表上使用 EC,即每月使用少于一次的数据。通过前面的分析,EC 减少了存储开销,但以降低性能速度为代价。因此,结合使用复制和 EC 文件存储方式才是明智之举。

在分区的 Hive 表中,对于频繁访问(热分区)和不频繁访问(冷分区)d 区,将冷分区转换为 EC 并保持热分区处于复制模式。

EC 策略

EC 策略决定了数据的编码和解码方式,它定义了数据的切片方式。EC 的默认策略是 RS-6-3-1024k:

  • RS - 编解码器 指定用于编码和解码的编解码器。RS 代表 Reed Solomon。这是目前唯一支持的编解码器类型。
  • 6 - 数据单元(data cells)的数量 设置数据被分割成多少块(数据单元)。在本例中,每个 stripe 中有 6 个数据单元格。
  • 3 - 奇偶校验单元(parity cells)的数量 设置计算和存储多少奇偶校验单元。奇偶校验单元用于数据恢复。当数据丢失或损坏时,奇偶校验单元用于解码和重构丢失的数据。在本例中,每个条带中有三个奇偶校验单元。
  • 1024k - 每个单元格的大小

查看 dfs.namenode.ec.system.default.policy 参数值,默认为 RS-6-3-1024k。

EC 支持的策略有:

  • RS-3-2-1024k
  • RS-6-3-1024k
  • RS-10-4-1024k

Hive 使用 EC 注意事项

  • 了解 EC 的局限性 不支持 XOR 编解码。不支持 HDFS 如下功能:hflush、hsync、concat、setReplication、truncate 和 append
  • CDH 集群版本 如果使用 CDH,确保 CDH 版本不低于 6.1.0。
  • 选择适合的 EC 策略
  • 考虑使用 EC 用于已经存在的数据还是新的数据
  • 验证集群设置是否满足 EC 对机架和节点的要求 在设置集群以利用 EC 时,充分考虑在机架和节点的数量。比如为了使 RS-6-3-1024k 策略具备节点容错的能力,至少需要 9 个 DataNode 节点。如果 DataNode 的数量小于策略要求数据块的数量,写操作就会失败。如果 DataNode 的数量小于策略要求的 ​​数据块数量+奇偶校验块​​ 的数量,写操作将成功,但是会显示一个警告消息。例如,对于 RS(6,3),如果有 6 到 8 个 DataNode,写操作将成功,但是会显示一个警告消息。如果少于 6 个 DataNode,写操作就会失败。对于机架级容错,必须有足够的机架数量,以使每个机架最多包含与 EC 奇偶校验块相同数量的块。机架的数量计算方式为 (data stripe width) / (number of parity blocks)。例如,对于 RS(6,3),机架的最小数量必须是 (6+3)/3 = 3。Cloudera 建议至少九个机架,这样每个机架上有一个数据或奇偶校验块。理想情况下,机架的数量超过数据和奇偶校验块的数量之和。

Hive 新表开启 EC

使用如下命令,在 Hive 新表上开启 EC:

hdfs ec -setPolicy -policy RS-3-2-1024k -path <table_location>

Hive 现有表开启 EC

可以在整个 Hive 表上或在一个 Hive 表的选定分区上开启 EC。提供两种方法来转换现有表的数据,如下面表格所示:

方法 A

方法 B

合并小文件

保持原始文件结构

提高整体性能

降低整体性能

转换时间长

转换时间短

针对方法 A 补充描述:

  • 合并小文件 这个过程将合并小文件并提高整体性能。如果表中有很多小文件,建议使用方法 A。
  • 提高整体性能 通过合并小文件,NameNode 需要处理的文件更少,整体集群性能得到提高。
  • 转换时间长 如果转换速度(数据转换到 EC)是最重要的因素,这不是最好的选择。

针对方法 B 补充描述:

  • 转换时间短 这种方法只复制数据一次,因此它的转换速度比方法 A 快得多。
  • 保持原始文件结构 如果表有很多小文件,这可能会有问题。
  • 降低整体性能 如果表中有很多小文件,单个 EC 文件比复制文件的开销更大,因为它们对 NameNode 的权重更大。使用转换方法 B,文件不会合并。

当在 Hive 表设置 EC 策略时,才会激活 EC。单独设置策略并不会将文件转换为 EC,仅针对 EC 的新文件,且现有文件保持不变。如果要将任何现有数据转换为 EC,必须将数据从目录中重写并返回到目录中。接下来笔者会讲解具体的操作步骤。

现有整个 Hive 表开启 EC

在现有整个 Hive 表上激活 EC,并将其内容从复制模式转换到 EC。

为了获得最佳性能,只在不经常访问的整个表上激活 EC。例如,针对该表,每月使用不到一次。

针对上面提到的两种方法进行操作,至于选择哪一种方法,读者自己掂量掂量。

方法 A: 将现有整个表转换为 EC

实战之前,打开两个终端,一个用于 Beeline 连接 Hive(简称 Beeline 终端),一个用于执行 HDFS Shell 命令的终端(简称 HDFS 终端)。

本示例使用根据日期进行分区的 Hive 表 hdfs_meta_dir_all_daily,数据量大小为:

$ hdfs dfs -du -s -h hdfs://nameservice1/user/hive/warehouse/hdfs_meta.db/hdfs_meta_dir_all_daily
222.1 M 666.2 M hdfs://nameservice1/user/hive/warehouse/hdfs_meta.db/hdfs_meta_dir_all_daily

另外我们查看一下目前的 HDFS EC 策略:

$ hdfs ec -listPolicies
Erasure Coding Policies:
ErasureCodingPolicy=[Name=RS-10-4-1024k, Schema=[ECSchema=[Codec=rs, numDataUnits=10, numParityUnits=4]], CellSize=1048576, Id=5], State=DISABLED
ErasureCodingPolicy=[Name=RS-3-2-1024k, Schema=[ECSchema=[Codec=rs, numDataUnits=3, numParityUnits=2]], CellSize=1048576, Id=2], State=DISABLED
ErasureCodingPolicy=[Name=RS-6-3-1024k, Schema=[ECSchema=[Codec=rs, numDataUnits=6, numParityUnits=3]], CellSize=1048576, Id=1], State=ENABLED
ErasureCodingPolicy=[Name=RS-LEGACY-6-3-1024k, Schema=[ECSchema=[Codec=rs-legacy, numDataUnits=6, numParityUnits=3]], CellSize=1048576, Id=3], State=DISABLED
ErasureCodingPolicy=[Name=XOR-2-1-1024k, Schema=[ECSchema=[Codec=xor, numDataUnits=2, numParityUnits=1]], CellSize=1048576, Id=4], State=ENABLED

因为实战部分要使用 RS-3-2-1024k 策略,所以我们需要开启它:

# 使用 hdfs keytab 访问
$ kinit -kt hdfs.keytab hdfs
$ hdfs ec -enablePolicy -policy RS-3-2-1024k
Erasure coding policy RS-3-2-1024k is enabled

如果 HDFS 不具备开启 EC 条件,会出现一些告警提示:

$ hdfs ec -enablePolicy -policy RS-3-2-1024k
Warning: The cluster setup does not support EC policy RS-3-2-1024k. Reason: 5 DataNodes are required for the erasure coding policies: RS-3-2-1024k. The number of DataNodes is only 4.



$ hdfs ec -enablePolicy -policy RS-3-2-1024k
Warning: The cluster setup does not support EC policy RS-3-2-1024k. Reason: 3 racks are required for the erasure coding policies: RS-3-2-1024k. The number of racks is only 1.

下面进行具体的操作步骤:

  1. 不再插入数据到表中
  2. 使用 Beeline 终端,备份临时表
0: jdbc:hive2://xx.xxx.xx.xxx:10000> create table hdfs_meta_dir_all_daily_tmp as select * from hdfs_meta_dir_all_daily;
  1. 切回到 HDFS 终端,在原始表上设置 EC 策略
$ hdfs ec -setPolicy -policy RS-3-2-1024k -path /user/hive/warehouse/hdfs_meta.db/hdfs_meta_dir_all_daily


Set RS-3-2-1024k erasure coding policy on /user/hive/warehouse/hdfs_meta.db/hdfs_meta_dir_all_daily
Warning: setting erasure coding policy on a non-empty directory will not automatically convert existing files to RS-3-2-1024k erasure coding policy
  1. 切回到 Beeline 终端,笔者实战选取的是分区表
# 设置动态分区表参数
0: jdbc:hive2://xx.xxx.xx.xxx:10000> set hive.exec.dynamic.partition=true;
0: jdbc:hive2://xx.xxx.xx.xxx:10000> set hive.exec.dynamic.partition.mode=nonstrict;


# 如果分区比较多,还需要设置如下两个参数
hive.exec.max.dynamic.partitions
hive.exec.max.dynamic.partitions.pernode


# 覆盖原始分区表数据
0: jdbc:hive2://xx.xxx.xx.xxx:10000> insert overwrite table hdfs_meta_dir_all_daily partition (dt) select * from hdfs_meta_dir_all_daily_tmp;

4.1 如果非分区表,直接覆盖表即可

0: jdbc:hive2://xx.xxx.xx.xxx:10000> insert overwrite table hdfs_meta_dir_all_daily select * from hdfs_meta_dir_all_daily_tmp;
  1. 删除临时表
0: jdbc:hive2://xx.xxx.xx.xxx:10000> drop table hdfs_meta_dir_all_daily_tmp;
  1. 查看表目录的 EC 配置
$ hdfs ec -getPolicy -path /user/hive/warehouse/hdfs_meta.db/hdfs_meta_dir_all_daily
RS-3-2-1024k
  1. 查看表的 block
$ hdfs fsck /user/hive/warehouse/hdfs_meta.db/hdfs_meta_dir_all_daily -files -blocks -locations

感兴趣的读者,可以分析返回的结果,这块内容笔者后续文章单独研究。

当然也可以针对表的某个分区进行转换为 EC,读者可以自行尝试,方法类似。

方法 B: 使用 HDFS distCp 将现有整个表转换为 EC

笔者这里使用 ssb 数据库下的 lineorder_3_replica 表进行测试,无分区。

$ hdfs dfs -du -s -h /user/hive/warehouse/ssb.db/lineorder_3_replica
1.1 G 3.3 G /user/hive/warehouse/ssb.db/lineorder_3_replica
  1. 不再插入数据到表中
0: jdbc:hive2://xx.xxx.xx.xxx:10000> lock table lineorder_3_replica exclusive;
  1. 移动表的存储目录到临时目录
$ hdfs dfs -mv /user/hive/warehouse/ssb.db/lineorder_3_replica /user/dataflow/ssb/data/lineorder_3_replica
  1. 恢复原始表的存储目录
$ hdfs dfs -mkdir /user/hive/warehouse/ssb.db/lineorder_3_replica
  1. 在原始表存储目录上设置 EC 策略
$ hdfs ec -setPolicy -policy RS-3-2-1024k -path /user/hive/warehouse/ssb.db/lineorder_3_replica


Set RS-3-2-1024k erasure coding policy on /user/hive/warehouse/ssb.db/lineorder_3_replica
  1. 将临时目录的内容重写回原始表目录 当数据拷贝时,它被转换成之前定义的 EC 策略:
$ hadoop distcp -overwrite -pb -skipcrccheck /user/dataflow/ssb/data/lineorder_3_replica /user/hive/warehouse/ssb.db/lineorder_3_replica
  1. 去掉之前加的表锁
0: jdbc:hive2://xx.xxx.xx.xxx:10000> unlock table lineorder_3_replica;
  1. 更新表的统计信息
0: jdbc:hive2://xx.xxx.xx.xxx:10000> analyze table lineorder_3_replica compute statistics;
  1. 确定无误后,删除临时目录
$ hdfs dfs -rm -r -skipTrash /user/dataflow/ssb/data/lineorder_3_replica
  1. 查看 EC 的存储
$ hdfs dfs -du -s -h /user/hive/warehouse/ssb.db/lineorder_3_replica
1.1 G 1.8 G /user/hive/warehouse/ssb.db/lineorder_3_replica

可见,副本数不再是 3 ,节约了存储。

Hadoop 3.x 时代,EC 露个脸呗!_HDFS_07

Hive 中 EC 相关工具

我们可以在 Hive 表中使用以下工具来查看有关 EC 文件的统计数据:

  1. desc formatted 命令
beeline> describe formatted <table_name>
  1. explain
beeline> explain <query>

对于已完成的 MapReduce 和 Spark 作业,它们的统计数据中包含一个用于 EC 字节读取的计数器:

  • 对于 MapReduce 作业,可以在查询的末尾找到这些作业的日志信息
  • 对于 Hive on Spark 作业,可以在 HS2 日志中找到这些作业的日志信息

总结

在本篇文章中,EC 理论部分参考了 data flair 文章,进行了整理总结,知识点相对比较容易理解。

有关 EC 的内容,笔者从理论到实战进行了较为详细的讲解,希望对读者有所帮助。另外有些内容,比如 EC 的元数据信息、数据块分布、数据块删除和恢复等内容,读者未在本篇文章中讲解,希望后续抽空时间再整理输出。

参考