写点什么

深入理解 HDFS(二):Replica

作者:冰心的小屋
  • 2023-08-06
    北京
  • 本文字数:6321 字

    阅读完需:约 21 分钟

深入理解 HDFS(二):Replica

对于我们上传的文件,HDFS 会复制多份,主要是为了降低数据丢失的风险,复制文件背后是复制 block,block 最终会存储在 datanode 中,那么在该过程中 namenode 参考了哪些因素选择的 datanode?内部处理流程又是如何?一起来分析。

1. 复制粒度

对于原始的 block 和复制的 block,统一叫做 replica,下面是和复制相关的一些细粒度参数:

  • dfs.blocksize: replica 的大小,默认 128m;

  • dfs.replication: 副本数量,默认 3,生成 replica 的过程是串行的,同一时刻只能有一个 writer;

  • dfs.replication.max:副本最大数量,默认 512,主要用于对 dfs.replication 的校验;

  • dfs.namenode.replication.min:副本最小数量,默认 1,主要用于 replica 生成后数量的校验。

2. 参考因素

从类别上可分为:客户端因素和服务端因素。


客户端因素:

  • CreateFlag.NO_LOCAL_WRITE:防止写入和客户端相同 IP 的 datanode,假设客户端和 datanode 在同一个主机上,客户端上传文件,默认策略下 datanode 都会存储 1 个副本,这样一来数据肯定不均衡,所以通过设置 NO_LOCAL_WRITE 可有效避免;

  • CreateFlag.NO_LOCAL_RACK:防止写入和客户端相同机架的 datanode,和 NO_LOCAL_WRITE 类似,只不过 datanode 换做成同机架下的所有 datanode 实例。

  • favoredNodes:如何服务端开启了 dfs.namenode.block-placement-policy.default.prefer-local-node 的设置,客户端上传文件时,通过 DistributedFileSystem.createFile -> HdfsDataOutputStreamBuilder.favoredNodes 进行设置,namenode 会根据上传的 favoredNodes 优先选择。


服务端因素:

  • dfs.block.replicator.classname:replication 分配策略,直接影响到 replication 分配到哪个 datanode 中,默认分配策略 BlockPlacementPolicyDefault,除此之外还有 BlockPlacementPolicyRackFaultTolerant 和 BlockPlacementPolicyWithNodeGroup 比较常用这里会重点讲解,其余的分配策略还有 AvailableSpaceBlockPlacementPolicy、AvailableSpaceRackFaultTolerantBlockPlacementPolicy 和 BlockPlacementPolicyWithUpgradeDomain,细节方面大家可查看下官方文档,后续我会酌情补充。

  • dfs.replication: 副本数量,默认 3,会决定选择 datanode 数量;

  • dfs.namenode.avoid.write.stale.datanode:不允许写入处于 stale 状态的 datanode,默认 false;

  • dfs.namenode.write.stale.datanode.ratio: 按照 stale 和 live 状态分别统计 datanode 数量,如果二者比值小于等于这个参数,即使设置了 dfs.namenode.avoid.write.stale.datanode = true ,也会选择处于 stale 状态下的 datanode。

3. Replica 分配策略

假设搭建了下面的集群,集群的副本数为 3:

3.1 BlockPlacementPolicyDefault:默认策略

3.1.1 未配置机架感知

所有 datanode 会默认分配到 /default-rack 虚拟机架中,屏蔽某些次要参数,该策略主要处理流程:

  1. 校验客户端所在节点是否有 datanode 实例:

  2. 如果有:选择该 datanode;

  3. 如果没有:从可用的 datanode 列表中随机选择 1 个。

  4. 对于每次选择的 datanode 都需要在可用列表中删除;

  5. 依次从可用列表中选择第 2 个、第 3 个。

3.1.2 机架感知

如果想开启 HDFS 机架感知,你需要做几件事:


  1. 编写脚本:主要目的是为了查询 datanode 所在机架。脚本只有 1 个输入参数主机 ip 或主机名,所以你需要根据主机 ip 或主机名输出匹配的机架信息,可参考下面的脚本进行配置:

#!/bin/bashdeclare -A topologytopology["172.17.48.1"]="rack1"topology["172.17.48.2"]="rack1"topology["172.17.48.3"]="rack2"topology["172.17.48.4"]="rack2"topology["172.17.48.5"]="rack3"topology["172.17.48.6"]="rack3"topology["datanode-001"]="rack1"topology["datanode-002"]="rack1"topology["datanode-003"]="rack2"topology["datanode-004"]="rack2"topology["datanode-005"]="rack3"topology["datanode-006"]="rack3"rack=${topology[$1]}[[ ${#rack} > 0 ]] && echo "/bj/$rack" || echo "/default-rack"
复制代码
  1. 在 core-site.xml 中配置 net.topology.script.file.name 的值为脚本的完整路径名;

<property>  <name>net.topology.script.file.name</name>  <value>/home/df66a0d7/topology.sh</value></property>
复制代码
  1. 重启集群后,输出集群拓补结构:hdfs dfsadmin -printTopology

从结果可以看出 HDFS 已经开启了机架感知,那接着分析处理流程:


  1. 校验客户端所在节点是否有 datanode 实例:

  2. 如果有:选择该 datanode;

  3. 如果没有:从可用的 datanode 列表中随机选择 1 个。

  4. 从第 1 个 datanode 所在机架外的其他机架,随机选择 1 个 datanode 作为第 2 个;

  5. 在第 2 个 datanode 所在机架随机选择 1 个作为第 3 个。

3.2 BlockPlacementPolicyRackFaultTolerant:贪婪策略

该策略的主要目是尽可能的在不同的机架上选择 datanode,主要处理流程:


  1. 通过调用 getMaxNodesPerRack 计算平均每个机架可选择的最大 datanode 数量;

  2. 根据计算的 maxNodesPerRack 调用 chooseTargetInOrder 一次性选择和副本数相同数量的 datanode;

  3. 为了保证选择的 datanode 和副本数相等,内部又做了很多保证机制。

如果决定使用贪婪策略,你需要编写脚本,开启机架感知功能,同时在 hdfs-site.xml 中需要设置属性:

<property>  <name>net.topology.script.file.name</name>  <value>/home/df66a0d7/topology.sh</value></property><property>  <name>dfs.block.replicator.classname</name>  <value>org.apache.hadoop.hdfs.server.blockmanagement.BlockPlacementPolicyRackFaultTolerant</value></property>
复制代码

3.3 BlockPlacementPolicyWithNodeGroup:分组策略

1 个 42U 19 英寸的机柜,除了预留散热和交,换机位置,可以轻松部署 16 台 1U 服务器。在同一个机架内,如果你想更细粒度的对 datanode 进行分组选择,可以试试分组策略。

分组策略内部处理流程:

  1. 校验客户端所在节点是否有 datanode 实例:

  2. 如果有:选择该 datanode;

  3. 如果没有:从可用的 datanode 列表中随机选择 1 个。

  4. 从第 1 个 datanode 所在机架外的其他机架,随机选择 1 个 datanode 作为第 2 个;

  5. 在第 2 个 datanode 所在机架不同的 node-group 中选择 1 个 作为第 3 个。

使用分组策略你需要进行如下操作:

  1. 开启机架感知:

#!/bin/bashdeclare -A topologytopology["172.17.48.1"]="/rack1/group11"topology["172.17.48.2"]="/rack1/group11"topology["172.17.48.3"]="/rack2/group21"topology["172.17.48.4"]="/rack2/group21"topology["172.17.48.5"]="/rack2/group22"topology["172.17.48.6"]="/rack2/group22"topology["datanode-001"]="/rack1/group11"topology["datanode-002"]="/rack1/group11"topology["datanode-003"]="/rack2/group21"topology["datanode-004"]="/rack2/group21"topology["datanode-005"]="/rack2/group22"topology["datanode-006"]="/rack2/group22"rack=${topology[$1]}[[ ${#rack} > 0 ]] && echo "$rack" || echo "/default-rack/group1"
复制代码
  1. 在 core-site.xml 中设置:

<property>  <name>net.topology.impl</name>  <value>org.apache.hadoop.net.NetworkTopologyWithNodeGroup</value></property><property>  <name>net.topology.nodegroup.aware</name>  <value>true</value></property>
复制代码
  1. 在 hdfs-site.xml 中设置:

<property>  <name>net.topology.script.file.name</name>  <value>/home/df66a0d7/topology.sh</value></property><property>  <name>dfs.block.replicator.classname</name>  <value>    org.apache.hadoop.hdfs.server.blockmanagement.BlockPlacementPolicyWithNodeGroup  </value></property>
复制代码

4. 快速搭建 HDFS

4.1 主机列表

172.17.48.7 namenode
172.17.48.1 datanode-001172.17.48.2 datanode-002172.17.48.3 datanode-003172.17.48.4 datanode-004172.17.48.5 datanode-005172.17.48.6 datanode-006
复制代码

4.2 跳转主机环境准备

我通常会选择 namenode 作为跳转主机,下面使用 root 用户操作:


# 确保当前用户为 172.17.48.10 root用户# 1. 生成密钥信息,一路回车即可ssh-keygen
# 2. 安装 pdsh 可以批量操作主机yum install -y pdsh
# 3. 将 namenode 的密钥复制到其他主机,包括自己的主机for i in `seq 1 7`do # 输出其他主机密码 ssh-copy-id 172.17.48.$idone
# 4. 编辑:/etc/hostsecho "172.17.48.7 namenode" >> /etc/hostsfor i in `seq 1 6`do echo "172.17.48.$i datanode-00$i" >> /etc/hostsdone
# 5. 编辑:/etc/profilevim /etc/profile
export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.372.b07-1.el7_9.x86_64export HADOOP_HOME=/home/5CC99CC1/hadoop-3.3.6export PATH=$PATH:$HOME/.local/bin:$HOME/bin:$JAVA_HOME/bin:$HADOOP_HOME/binexport ips="172.17.48.1,172.17.48.2,172.17.48.3,172.17.48.4,172.17.48.5,172.17.48.6,172.17.48.7"
alias n1="ssh namenode"alias d1="ssh datanode-001"alias d2="ssh datanode-002"alias d3="ssh datanode-003"alias d4="ssh datanode-004"alias d5="ssh datanode-005"alias d6="ssh datanode-007"
# 6. 传送给其他节点for i in `seq 1 7`do scp /etc/hosts 172.17.48.$i:/etc/hosts scp /etc/profile 172.17.48.$i:/etc/profiledone
复制代码

4.3 安装 JDK

在跳转机上使用 root 用户操作。


#!/bin/bash# 关闭防火墙,ips 参考 4.2pdsh -w $ips "systemctl stop firewalld"pdsh -w $ips "systemctl disable firewalld"
# 安装JDKpdsh -w $ips "echo y | yum install java-1.8.0-openjdk-devel"
# 添加用户,最好随机生成用户名和密码,避免使用 hadoop:hadoop,很容易被破解pdsh -w $ips "adduser -m 5CC99CC1 -G wheel"pdsh -w $ips "echo '5CC99CC1:CFA02D2C68D5'|chpasswd"
复制代码

4.4 安装 HDFS

在跳转机上使用 5CC99CC1 用户操作。


#!/bin/bash# 1. 通用NAME=hadoop-3.3.6FILE=$NAME.tar.gzHOME=/home/5CC99CC1TMP_HOME=$HOME/dfs/tmp
# 2. NameNodeNAME_NODE_NAME=hadoop-3.3.6NAME_NODE_HOST=namenodeNAME_NODE_PORT=8020NAME_NODE_STORAGE=$HOME/dfs/nameNAME_NODE2_STORAGE=$HOME/dfs/namesecondary
# 3. DataNodeDATA_NODE_NAME=hadoop-3.3.6DATA_NODE_HOST=datanode-001,datanode-002,datanode-003,datanode-004,datanode-005,datanode-006DATA_NODE_STORAGE=$HOME/dfs/dataDATA_NODE_REPLICATION=3
# 4. 安装 DataNode target=$(cd "$(dirname "$0")"; pwd)cd $target
# 5. 处理安装包if [ ! -f $FILE ]then wget https://mirrors.aliyun.com/apache/hadoop/common/hadoop-3.3.6/hadoop-3.3.6.tar.gz?spm=a2c6h.25603864.0.0.2e278a13NqaPHe -O hadoop-3.3.6.tar.gzfi
tar -xvf $FILE
# 6. 设置 core-site.xmlcat << EOF > $NAME/etc/hadoop/core-site.xml<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="configuration.xsl"?><!-- Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See accompanying LICENSE file.-->
<!-- Put site-specific property overrides in this file. --><configuration> <property> <name>fs.defaultFS</name> <value>hdfs://$NAME_NODE_HOST:$NAME_NODE_PORT</value> </property> <property> <name>hadoop.tmp.dir</name> <value>$TMP_HOME</value> </property></configuration>EOF
# 7. 设置 hdfs-site.xmlcat << EOF > $NAME/etc/hadoop/hdfs-site.xml<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="configuration.xsl"?><!-- Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. See accompanying LICENSE file.-->
<!-- Put site-specific property overrides in this file. --><configuration> <property> <name>dfs.namenode.name.dir</name> <value>$NAME_NODE_STORAGE</value> </property> <property> <name>dfs.namenode.checkpoint.dir</name> <value>$NAME_NODE2_STORAGE</value> </property> <property> <name>dfs.datanode.data.dir</name> <value>$DATA_NODE_STORAGE</value> </property> <property> <name>dfs.replication</name> <value>$DATA_NODE_REPLICATION</value> </property></configuration>EOF
# 8. 设置环境变量 hadoop-env.shHADOOP_ENV=$NAME/etc/hadoop/hadoop-env.shecho "JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.372.b07-1.el7_9.x86_64" >> $HADOOP_ENV# 可以方便调试 namenode 和 datanodeecho 'export HDFS_NAMENODE_OPTS="-Xmx3g -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9999"' >> $HADOOP_ENVecho 'export HDFS_DATANODE_OPTS="-Xmx3g -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=9998"' >> $HADOOP_ENV
# 9. 同步 datanode 节点mv $NAME $DATA_NODE_NAMEfor node in ${DATA_NODE_HOST//,/ }do scp -r $DATA_NODE_NAME $node:$HOMEdone
# 10. 同步 namenode 节点for node in ${DATA_NODE_HOST//,/ }do echo $node >> $DATA_NODE_NAME/etc/hadoop/workers.tmpdonemv $DATA_NODE_NAME/etc/hadoop/workers.tmp $DATA_NODE_NAME/etc/hadoop/workersmv $DATA_NODE_NAME $NAME_NODE_NAMEscp -r $NAME_NODE_NAME $NAME_NODE_HOST:$HOME
复制代码

4.5 启动 HDFS

在跳转机上使用 5CC99CC1 用户操作。


# format $HADOOP_HOME/bin/hdfs namenode -format# 启动 namenode $HADOOP_HOME/bin/hdfs --daemon start namenode
# 启动 datanodepdsh -w $ips "/home/5CC99CC1/hadoop-3.3.6/bin/hdfs --daemon start datanode"
复制代码

4.6 模拟上传测试

使用 root 用户安装 fio


yum install -y fio
复制代码


脚本功能:生成指定大小文件上传到 HDFS,之后查看该文件的 block 分配情况。


#!/bin/bash# 上传 1m 文件:sh upload.sh 1mfio -filename=$1 -direct=1 -ioengine=libaio -bs=4k -size=$1 -numjobs=1 -iodepth=16 -runtime=1 -thread -rw=write -group_reporting -name="write_test"hdfs dfs -copyFromLocal $1 /tmphdfs fsck /tmp/$1  -files -locations -blocks
复制代码


有问题欢迎大家留言,我们一起讨论。


发布于: 刚刚阅读数: 6
用户头像

分享技术上的点滴收获! 2013-08-06 加入

一杯咖啡,一首老歌,一段代码,欢迎做客冰屋,享受编码和技术带来的快乐!

评论

发布
暂无评论
深入理解 HDFS(二):Replica_hdfs_冰心的小屋_InfoQ写作社区