HDFS高可用架构涉及常用功能整理
探讨hdfs的系统架构以及以及整体常用的命令和系统分析,本文主要探讨高可用版本的hdfs集群,并基于日常工作中的沉淀进行思考和整理。更多关于分布式系统的架构思考请参考文档关于常见分布式组件高可用设计原理的理解和思考
1. hdfs的高可用系统架构和相关组件
NameNode 的高可用发展史在 Hadoop2.0 以前,每个 HDFS 集群只有一个 NameNode,一旦这个节点不可用,则整个HDFS集群将处于不可用状态(即HDFS2.0以前,NameNode存在单点故障风险)。 在HDFS集群中同时运行两个NameNode,通过HA的方式进行集群切换,从而达到高可用的目的。
NN节点有如下2种状态:
- Active(活跃)状态:负责集群中所有客户端的操作(修改命名空间、删除备份数据块等操作);
- Standby(备份)状态:充当从服务器,和 Active NameNode 有相同的命名空间和元数据。
当 Active NameNode 停止服务时,Standby NameNode 能够快速进行故障切换,以保证 HDFS 集群服务不受影响。
hdfs的系统架构如下
相关核心的组件和角色作用如下
组件 | 部署模式 | 组件作用 | 备注 |
---|---|---|---|
NN(active) | 单机部署 | 存储集群的元数据,具体集群数据的全局视角 | 给客户端提供请求服务等,和standby节点进行形成主备 |
NN(standby) | 单机部署 | 存储集群的元数据,具体集群数据的全局视角 | 配合active完成checkpoints,合并editlog和fimage,和active节点进行形成主备 |
zk | 多节点部署 | zk提供hdfs的NN选主锁和消息通知,zkfc接受相关zk进行主从切换 | 通过Zab 协议来保证分布式事务的最终一致性 |
zkfc | 和NN部署 | 和zk交互,通过zk的消息通知,并调用NN的rpc接口实现集群选主和切主 | fencing NN,防止出现多active形成脑裂 |
jn | 多节点部署 | active和standby节点的editlog数据桥梁,通过rpc完成数据传输 | 通过paxos协议选主,建议单数节点部署(3/5/7等) |
DN | 单机部署 | 数据的存储节点,会定期上报心跳和当前节点的block信息给NN | 保存当前节点的块信息,并监控块状态 |
2. hdfs的核心参数
2.1 常规配置
hdfs-site.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
Licensed ...
-->
<configuration>
<!-- 定义hdfs的namespace,可以配置多个,使用","分隔 -->
<property>
<name>dfs.nameservices</name>
<value>cluster</value>
</property>
<!-- 定义hdfs的NN节点 -->
<property>
<name>dfs.ha.namenodes.cluster</name>
<value>namenode1,namenode2</value>
</property>
<!-- 定义hdfs的副本数量 -->
<property>
<name>dfs.replication</name>
<value>3</value>
</property>
<!-- 定义是否开启web监控页面 -->
<property>
<name>dfs.webhdfs.enabled</name>
<value>true</value>
</property>
<property>
<name>dfs.permissions</name>
<value>false</value>
</property>
<property>
<name>dfs.permissions.enabled</name>
<value>false</value>
</property>
<!-- 定义hdfs的NN的editlog保存路径 -->
<property>
<name>dfs.namenode.name.dir</name>
<value>file:/data2/hadoop/dfs/name</value>
</property>
<!-- 定义hdfs的NN1,NN2节点信息 -->
<property>
<name>dfs.namenode.rpc-address.cluster.namenode1</name>
<value>xx.xx.xx.xx:9000</value>
</property>
<property>
<name>dfs.namenode.rpc-address.cluster.namenode2</name>
<value>xx.xx.xx.xx:9000</value>
</property>
<property>
<name>dfs.namenode.servicerpc-address.cluster.namenode1</name>
<value>namenode1:53310</value>
</property>
<property>
<name>dfs.namenode.servicerpc-address.cluster.namenode2</name>
<value>namenode2:53310</value>
</property>
<property>
<name>dfs.namenode.http-address.cluster.namenode1</name>
<value>xx.xx.xx.xx:50070</value>
</property>
<property>
<name>dfs.namenode.http-address.cluster.namenode2</name>
<value>xx.xx.xx.xx:50070</value>
</property>
<property>
<name>dfs.namenode.https-address.cluster.namenode1</name>
<value>xx.xx.xx.xx:9871</value>
</property>
<property>
<name>dfs.namenode.https-address.cluster.namenode2</name>
<value>xx.xx.xx.xx:9871</value>
</property>
<!-- 定义hdfs的jn节点信息 -->
<property>
<name>dfs.namenode.shared.edits.dir</name>
<value>qjournal://xx.xx.xx.xx:8485;xx.xx.xx.xx:8485;xx.xx.xx.xx:8485/cluster</value>
</property>
<property>
<name>dfs.journalnode.edits.dir</name>
<value>/data2/hadoop/journal</value>
</property>
<property>
<name>dfs.client.failover.proxy.provider.cluster</name>
<value>org.apache.hadoop.hdfs.server.namenode.ha.ConfiguredFailoverProxyProvider</value>
</property>
<!-- 定义hdfs的NN的fencing信息,NN通过切主时会获取原主的信息,如果调用rpc原主降为standby失败,就会通过fencing ssh到对应的服务器杀死原主的NN进程 -->
<property>
<name>dfs.ha.fencing.methods</name>
<value>sshfence(root:36000)
shell(/bin/true)</value>
</property>
<property>
<name>dfs.ha.fencing.ssh.private-key-files</name>
<value>/root/.ssh/id_rsa</value>
</property>
<property>
<name>dfs.ha.fencing.ssh.coNNect-timeout</name>
<value>30000</value>
</property>
<property>
<name>dfs.ha.automatic-failover.enabled</name>
<value>true</value>
</property>
<property>
<name>ha.failover-controller.cli-check.rpc-timeout.ms</name>
<value>60000</value>
</property>
<property>
<name>ipc.client.coNNect.timeout</name>
<value>60000</value>
</property>
<!-- 定义hdfs的DN数据保存目录,可以配置多个路径,用","分割 -->
<property>
<name>dfs.datanode.data.dir</name>
<value>/hdfsdata1/hadoop/dfs/data</value>
</property>
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>xx.xx.xx.xx:9868</value>
</property>
<property>
<name>dfs.namenode.secondary.https-address</name>
<value>xx.xx.xx.xx:9869</value>
</property>
<property>
<name>dfs.namenode.datanode.registration.ip-hostname-check</name>
<value>false</value>
</property>
<property>
<name>dfs.namenode.rpc-bind-host</name>
<value>0.0.0.0</value>
</property>
<!-- 定义hdfs的DN内部的平衡、同步fimage的带宽限制 -->
<property>
<name>dfs.image.transfer.bandwidthPerSec</name>
<value>52428800</value>
</property>
<property>
<name>dfs.datanode.balance.bandwidthPerSec</name>
<value>52428800</value>
</property>
<property>
<name>dfs.datanode.balance.max.concurrent.moves</name>
<value>50</value>
</property>
<!-- 定义hdfs的需要下线的机器列表 -->
<property>
<name>dfs.hosts.exclude</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/excludes_datanodes</value>
</property>
<property>
<name>dfs.namenode.http-bind-host</name>
<value>0.0.0.0</value>
</property>
<property>
<name>dfs.namenode.https-bind-host</name>
<value>0.0.0.0</value>
</property>
<!-- 定义hdfs的zk路径,通过在该路径下创建临时节点选主 -->
<property>
<name>ha.zookeeper.parent-znode</name>
<value>/hadoop-hdfs-ha</value>
</property>
<property>
<name>dfs.datanode.https.address</name>
<value>0.0.0.0:9865</value>
</property>
<property>
<name>dfs.block.access.token.enable</name>
<value>true</value>
</property>
<!-- 定义hdfs的kerberos鉴权,如果设置鉴权可以不配置 -->
<property>
<name>dfs.namenode.keytab.file</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/hdfs.keytab</value>
</property>
<!-- 定义hdfs的kerberos用户,_HOST代表本机的hostname,因此没台DN的用户hdfs/_HOST@XX.COM有差别,需要针对每个DN单独配置证书 -->
<property>
<name>dfs.namenode.kerberos.principal</name>
<value>hdfs/_HOST@XX.COM</value>
</property>
<property>
<name>dfs.namenode.kerberos.https.principal</name>
<value>HTTP/_HOST@XX.COM</value>
</property>
<property>
<name>dfs.namenode.kerberos.internal.spnego.principal</name>
<value>HTTP/_HOST@XX.COM</value>
</property>
<property>
<name>dfs.datanode.data.dir.perm</name>
<value>755</value>
</property>
<property>
<name>dfs.datanode.address</name>
<value>0.0.0.0:9866</value>
</property>
<property>
<name>dfs.datanode.keytab.file</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/hdfs.keytab</value>
</property>
<property>
<name>dfs.datanode.kerberos.principal</name>
<value>hdfs/_HOST@XX.COM</value>
</property>
<property>
<name>dfs.datanode.kerberos.https.principal</name>
<value>HTTP/_HOST@XX.COM</value>
</property>
<property>
<name>dfs.journalnode.keytab.file</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/hdfs.keytab</value>
</property>
<property>
<name>dfs.journalnode.kerberos.principal</name>
<value>hdfs/_HOST@XX.COM</value>
</property>
<property>
<name>dfs.journalnode.kerberos.internal.spnego.principal</name>
<value>HTTP/_HOST@XX.COM</value>
</property>
<property>
<name>dfs.web.authentication.kerberos.principal</name>
<value>HTTP/_HOST@XX.COM</value>
</property>
<property>
<name>dfs.web.authentication.kerberos.keytab</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/hdfs.keytab</value>
</property>
<property>
<name>dfs.data.transfer.protection</name>
<value>integrity</value>
</property>
<!-- 定义hdfs的DN的最大文件数量 -->
<property>
<name>dfs.namenode.fs-limits.max-directory-items</name>
<value>3200000</value>
</property>
</configuration>
core-site.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="configuration.xsl"?>
<!--
Licensed ...
-->
<configuration>
<!-- 定义hdfs的zk配置 -->
<property>
<name>ha.zookeeper.auth</name>
<value>digest:zk_user:zk_passwd</value>
</property>
<property>
<name>ha.zookeeper.acl</name>
<value>digest:zk_user:Yg6OG5Tas/LEH5bd73noFMYG3xo=:rwcda</value>
</property>
<property>
<name>hadoop.http.filter.initializers</name>
<value>org.apache.hadoop.security.AuthenticationFilterInitializer</value>
</property>
<property>
<name>hadoop.http.authentication.type</name>
<value>kerberos</value>
</property>
<property>
<name>hadoop.http.authentication.signature.secret.file</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/secret</value>
</property>
<property>
<name>hadoop.http.authentication.simple.anonymous.allowed</name>
<value>false</value>
</property>
<!-- 定义hdfs的kerberos认证信息 -->
<property>
<name>hadoop.security.authentication</name>
<value>kerberos</value>
</property>
<property>
<name>hadoop.security.authorization</name>
<value>true</value>
</property>
<property>
<name>hadoop.http.authentication.kerberos.principal</name>
<value>HTTP/_HOST@XX.COM</value>
</property>
<property>
<name>hadoop.http.authentication.kerberos.keytab</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/hdfs.keytab</value>
</property>
<property>
<name>hadoop.tmp.dir</name>
<value>file:/data2/hadoop/tmp</value>
</property>
<!-- 定义hdfs的namespace集群信息,和hdfs-site.xml定义一致 -->
<property>
<name>fs.defaultFS</name>
<value>hdfs://cluster</value>
</property>
<property>
<name>io.file.buffer.size</name>
<value>131072</value>
</property>
<property>
<name>hadoop.proxyuser.hadoop.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.hadoop.groups</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.mapred.hosts</name>
<value>*</value>
</property>
<property>
<name>hadoop.proxyuser.mapred.groups</name>
<value>*</value>
</property>
<!-- 定义hdfs的zk访问地址 -->
<property>
<name>ha.zookeeper.quorum</name>
<value>xx.xx.xx.xx:2181,xx.xx.xx.xx:2181,xx.xx.xx.xx:2181</value>
</property>
<property>
<name>ha.zookeeper.session-timeout.ms</name>
<value>30000</value>
</property>
<!-- 定义hdfs的机架topo配置 -->
<property>
<name>topology.script.file.name</name>
<value>/usr/local/services/hadoop-3.2.1/etc/hadoop/hdfs_rack_info.py</value>
</property>
<!-- 定义hdfs的DN 汇报心跳配置 -->
<property>
<name>ipc.client.coNNect.max.retries</name>
<value>10</value>
</property>
<property>
<name>ipc.client.coNNect.retry.interval</name>
<value>5000</value>
</property>
<property>
<name>ipc.client.coNNect.max.retries.on.timeouts</name>
<value>3</value>
</property>
</configuration>
2.2 特殊优化配置
2.1.1 NN优化
对于NN来说,最大的问题还是响应客户端的rpc请求,由于只能单点响应客户端请求,因此单个NN的需要进行系统优化响应服务请求。
1, dfs.namenode.handler.count
参数:namenode的服务器线程数。
NameNode有一个工作线程池用来处理客户端的远程过程调用及集群守护进程的调用。处理程序数量越多意味着要更大的池来处理来自不同DataNode的并发心跳以及客户端并发的元数据操作。对于大集群或者有大量客户端的集群来说,通常需要增大参数dfs.namenode.handler.count的默认值10。设置该值的一般原则是将其设置为集群大小的自然对数乘以20,即20logN,N为集群大小。
<property>
<name>dfs.namenode.handler.count</name>
<value>200</value>
</property>
2.1.2 DN优化
1, dfs.datanode.balance.bandwidthPerSec
参数: datanode 平衡带宽
描述:指定每个datanode可以利用每秒字节数来平衡目标的最大带宽。
<property>
<name>dfs.datanode.balance.bandwidthPerSec</name>
<value>52428800</value>
</property>
2,dfs.datanode.max.transfer.threads
参数:datanode 最大传输线程数
描述:指定用于传输数据进出DN的最大线程数。集群中如果不一致,会造成数据分布不均。
<property>
<name>dfs.datanode.max.transfer.threads</name>
<value>100</value>
</property>
2.1.3 节点退役、balance参数优化
参数名 | 文件 | 默认值 | 建议值 | 作用 |
---|---|---|---|---|
dfs.namenode.replication.max-streams | hdfs-site.xml | 2 | 20 | balance/退服性能参数,单个DataNode最大同时恢复的块数量,可以间接控制DataNode恢复数据块的带来的网络等压力。需要与dfs.namenode.replication.work.multiplier.per.iteration配置项配合使用 |
dfs.namenode.replication.max-streams-hard-limit | hdfs-site.xml | 4 | 40 | balance/退服性能参数,最高优先级复制流的数量的硬限制 |
dfs.namenode.replication.work.multiplier.per.iteration | hdfs-site.xml | 2 | 10 | balance/退服性能参数,决定了可以从很多under replication blocks中选出多少个block 准备进行复制。如果该参数配置得太小,则dfs.namenode.replication.max-streams配置得再大没有用;可以选出的block数与集群live 的datadnode 成正比。 |
dfs.datanode.balance.bandwidthPerSec | hdfs-site.xml | 2 | 524288000 | balance/退服性能参数,决定了可以从很多under replication blocks中选出多少个block 准备进行复制。如果该参数配置得太小,则dfs.namenode.replication.max-streams配置得再大没有用;可以选出的block数与集群live 的datadnode 成正比。 |
dfs.balance.bandwidthPerSec | hdfs-site.xml | 2 | 524288000 | balance/退服性能参数,决定了可以从很多under replication blocks中选出多少个block 准备进行复制。如果该参数配置得太小,则dfs.namenode.replication.max-streams配置得再大没有用;可以选出的block数与集群live 的datadnode 成正比。 |
dfs.datanode.balance.max.concurrent.moves | hdfs-site.xml | 5 | 512 | balance/退服性能参数,单个datanode允许同时移动最大块数 |
2.1.4 连接超时优化
参数名 | 文件 | 默认值 | 建议值 | 作用 |
---|---|---|---|---|
ipc.client.connection.maxidletime | core-site.xml | 10000 | 20000 | RCP性能优化 |
ipc.client.connect.timeout | core-site.xml | 20000 | 60000 | RCP性能优化,客户端等待套接字建立服务器连接的毫秒数。 |
ipc.client.connect.max.retries | core-site.xml | 10 | 20 | RCP性能优化,客户端为建立服务器连接而重试的次数。 |
ipc.client.connect.max.retries.on.timeouts | core-site.xml | 45 | 90 | RCP性能优化,客户端在套接字超时时为建立服务器连接而重试的次数 |
ipc.server.listen.queue.size | core-site.xml | 128 | 20480 | RCP性能优化,接受客户端连接的服务器的侦听队列的长度。 |
3. hdfs常用命令
hdfs dfs
下面为显示的内容:
Usage: hadoop fs [generic options]
[-appendToFile <localsrc> ... <dst>]
[-cat [-ignoreCrc] <src> ...]
[-checksum <src> ...]
[-chgrp [-R] GROUP PATH...]
[-chmod [-R] <MODE[,MODE]... | OCTALMODE> PATH...]
[-chown [-R] [OWNER][:[GROUP]] PATH...]
[-copyFromLocal [-f] [-p] [-l] <localsrc> ... <dst>]
[-copyToLocal [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
[-count [-q] [-h] <path> ...]
[-cp [-f] [-p | -p[topax]] <src> ... <dst>]
[-createSnapshot <snapshotDir> [<snapshotName>]]
[-deleteSnapshot <snapshotDir> <snapshotName>]
[-df [-h] [<path> ...]]
[-du [-s] [-h] <path> ...]
[-expunge]
[-find <path> ... <expression> ...]
[-get [-p] [-ignoreCrc] [-crc] <src> ... <localdst>]
[-getfacl [-R] <path>]
[-getfattr [-R] {-n name | -d} [-e en] <path>]
[-getmerge [-nl] <src> <localdst>]
[-help [cmd ...]]
[-ls [-d] [-h] [-R] [<path> ...]]
[-mkdir [-p] <path> ...]
[-moveFromLocal <localsrc> ... <dst>]
[-moveToLocal <src> <localdst>]
[-mv <src> ... <dst>]
[-put [-f] [-p] [-l] <localsrc> ... <dst>]
[-renameSnapshot <snapshotDir> <oldName> <newName>]
[-rm [-f] [-r|-R] [-skipTrash] <src> ...]
[-rmdir [--ignore-fail-on-non-empty] <dir> ...]
[-setfacl [-R] [{-b|-k} {-m|-x <acl_spec>} <path>]|[--set <acl_spec> <path>]]
[-setfattr {-n name [-v value] | -x name} <path>]
[-setrep [-R] [-w] <rep> <path> ...]
[-stat [format] <path> ...]
[-tail [-f] <file>]
[-test -[defsz] <path>]
[-text [-ignoreCrc] <src> ...]
[-touchz <path> ...]
[-truncate [-w] <length> <path> ...]
[-usage [cmd ...]]
3.1 常用基础命令
整理日常操作hdfs常用的命令,便于针对hdfs的文件操作
1, –ls:查看指定目录下内容
eg:hadoop fs –ls /user/wangwu
2, –cat:显示文件内容
eg:hadoop fs -cat /user/wangwu/data.txt
3, –put:将本地文件存储至hadoop
eg:hadoop fs –put /home/t/file.txt /user/t
4, –put:将本地文件夹存储至hadoop
eg:hadoop fs –put /home/t/dir_name /user/t
5, -get:将hadoop上某个文件down至本地已有目录下
eg:hadoop fs –get /user/t/ok.txt /home/t
6, –rm:删除hadoop上指定文件或文件夹
eg:hadoop fs –rm /user/t/ok.txt
7, 删除hadoop上指定文件夹(包含子目录等)
eg:hadoop fs –rm /user/t
8, –mkdir:在hadoop指定目录内创建新目录
eg:hadoop fs –mkdir /user/t
9, -touchz:在hadoop指定目录下新建一个空文件
eg:hadoop fs -touchz /user/new.txt
10, –mv:将hadoop上某个文件重命名
eg:hadoop fs –mv /user/test.txt /user/ok.txt
11, -setrep:设置HDFS中文件的副本数量
eg:hadoop fs -setrep 10 /tmp/tt/student.txt
12, 将正在运行的hadoop作业kill掉
eg:hadoop job –kill [job-id]
13, -help:输出这个命令参数
eg:hadoop fs -help rm
14, -moveFromLocal:从本地剪切粘贴到HDFS
eg:hadoop fs -moveFromLocal ./stuDNet.txt /tmp/test/
15, -appendToFile:追加一个文件到已经存在的文件末尾
eg:hadoop fs -appendToFile liubei.txt /sanguo/shuguo/zhangsan.txt
16, -chgrp , -chmod, -chown:Linux文件系统中的用法一样,修改文件所属权限
eg:hadoop fs -chmod 666 /sanguo/shuguo/zhangsan.txt
17, -copyFromLocal:从本地文件系统中拷贝文件到HDFS路径去
eg:hadoop fs -copyFromLocal README.txt /
18, -copyToLocal:从HDFS拷贝到本地
eg:hadoop fs -copyToLocal /sanguo/shuguo/zhangsan.txt ./
19, -cp :从HDFS的一个路径拷贝到HDFS的另一个路径
eg:hadoop fs -cp /sanguo/shuguo/zhangsan.txt /zhuge.txt
20, -tail:显示一个文件的末尾
eg:hadoop fs -tail /sanguo/shuguo/zhangsan.txt
21, -rmdir:删除空目录
eg:hadoop fs -rmdir /test
22, -du:统计文件夹的大小信息
eg:hadoop fs -du -h /user/itcast/test
1.3 K /user/itcast/test/README.txt
15 /user/itcast/test/jinlian.txt
1.4 K /user/itcast/test/nihao.txt
3.2 常用运维命令
用于日常运维命令,便于进行服务运维,提升系统稳定性。
1, 设置文件副本数量
如果hdfs的磁盘使用率过高,可以临时调整大文件副本,注意如果调整的副本数量过小,可能会面临数据可靠性风险,慎重!
hadoop fs -setrep 10 /sanguo/shuguo/kongming.txt
2, 检查hdfs的是否有坏块或者副本缺失
hdfs fsck / -list-corruptfileblocks
3, 检查hdfs的文件是否有坏块或者副本缺失
hdfs fsck / -files -blocks -locations
hdfs fsck /user/root/hello/yarn-demo-1.0-SNAPSHOT-jar-with-dependencies.jar -files -blocks -locations
也可以通过该命令查看对应的文件的block所在的位置
3, 执行hdfs执行格式化
执行格式化操作,会清理当前集群的所有数据,重新创建一个新的集群,一般只在新部署hdfs集群时使用,一定要慎重!!!
# 格式化zk,会在zk上创建临时目录,用于选主
hdfs zkfc -formatZK
# 格式化整个hdfs系统
hdfs namenode -format
4, 同步active的元数据
完成active部署后,standby需要跟active同步元数据,才能达成一致,该操作需要再standby节点执行,否则可能会造成元数据丢失.
hdfs namenode -bootstrapStandby
5, hdfs安全模式
当hdfs集群的数据副本丢失过多,或者集群存活的DN数量到达阈值时,就会主动进入安全模式,集群只读,需要人工修复。
# 检查是否进入安全模式
hdfs dfsadmin -safemode get
# 如何离开安全模式
hdfs dfsadmin -safemode leave
6,强行切换NN
# 强行指定某个节点为active,会触发fencing,把老active的节点杀死
hdfs haadmin -transitionToActive -forcemanual -forceactive namenode1
# 强行指定某个节点为standby
hdfs haadmin -transitionToStandby -forcemanual namenode1
7, 获取nn的集群节点状态
# 获取所有的nn节点状态
hdfs haadmin -getAllServiceState
#获取单个nn的节点状态
hdfs haadmin -getServiceState nn1
hdfs haadmin -getServiceState nn2
5, 刷新节点信息
通常使用于下线节点
# 配置完exclude后,通过手动刷新配置生效
hdfs dfsadmin -refreshNodes
4. 事务性
4.1 数据写流程
hdfs的数据写流程,整体流程如下。
客户端写入的总体流程如下
- 客户端向NameNode发出写文件请求。
- 检查是否已存在文件、检查权限。若通过检查,直接先将操作写入EditLog,并返回输出流对象。(注:WAL,write ahead log,先写Log,再写内存,因为EditLog记录的是最新的HDFS客户端执行所有的写操作。如果后续真实写操作失败了,由于在真实写操作之前,操作就被写入EditLog中了,故EditLog中仍会有记录,我们不用担心后续client读不到相应的数据块,因为在第5步中DataNode收到块后会有一返回确认信息,若没写成功,发送端没收到确认信息,会一直重试,直到成功)
- client端按block切分文件(通常一个block是128M)
- client将NameNode返回的分配的可写的DataNode列表和Data数据一同发送给最近的第一个DataNode节点,此后client端和NameNode分配的多个DataNode构成pipeline管道,client端向输出流对象中写数据。client每向第一个DN写入一个packet,这个packet便会直接在pipeline里传给第二个、第三个…DataNode(满足一个packet就会发送数据,而不是一个block)
- 在pipeline反方向上,逐个发送ack(命令正确应答),最终由pipeline中第一个DataNode节点将ack发送给client。(注:并不是每写完一个packet后就返回确认信息,个人觉得因为packet中的每个chunk都携带校验信息,没必要每写一个就汇报一下,这样效率太慢。正确的做法是写完一个block块后,对校验信息进行汇总分析,就能得出是否有块写错的情况发生)
- 写完数据,关闭输输出流。
- 发送完成信号给NameNode。
这里面有几个关键概念,需要进行整理
- block
这个大家应该知道,文件上传前需要分块,这个块就是block,一般为128MB(可以改,但是不推荐)。client完成1个block写入后,所有的DN返回后,进行一次数据确认。 - packet
packet是第二大的单位,它是client端向DataNode,或DataNode的PipLine之间传数据的基本单位,默认64KB。当客户端的输出流(outputstream)满足1个packet后,就会开始进行数据发送给DN。 - chunk
chunk是最小的单位,它是client向DataNode,或DataNode的PipLine之间进行数据校验的基本单位,默认512Byte,因为用作校验,故每个chunk需要带有4Byte的校验位。所以实际每个chunk写入packet的大小为516Byte。由此可见真实数据与校验值数据的比值约为128 : 1(即64*1024 / 512)
客户端在给DN发送数据时,
- 以chunk单位进行数据校验,完成chunk(516Bytes)数据校验后,写入到packet(64KB)
- 当64*1024 / 512=128个chunk写入到packet后,packet满足要求,并输出到outputstream发送给DN
- 多个DN之间形成pipeline,并以packet作为基础单元接受数据,DN以3->2->1的顺序反向确认消息返回ack
- 当1个block(128MB/64KB=2048个packet)写入,并所有的DN返回ack后,统一返回给client,确认1个block数据写完成
4.2 数据读流程
读相对于写,简单一些
读详细步骤:
- 客户端访问NameNode,查询元数据信息,获得这个文件的数据块位置列表,返回输入流对象。
- 客户端就近挑选一台datanode服务器,请求建立输入流 。
- DataNode向输入流中中写数据,以packet为单位来校验。
5. 疑问和思考
5.1. hdfs集群初始化部署流程?
5.2. hdfs是如何选择DN进行数据读写的?
Hadoop 默认的副本数为3,并且在机架的存放上也有一定的策略。优先按照如下策略选择合适的DN节点:
(1)第 1 个副本存放在 HDFS 客户端所在的节点上。
(2)第 2 个副本存放在与第1个副本不同的机架上,并且是随机选择的节点。
(3)第 3 个副本存放在与第2个副本相同的机架上,并且是不同的节点。
5.3. hdfs在读写过程中如果出现异常该如何处理?
1,如果DN收到一半 DN挂了了的处理流程
client发送数据以block作为基础单元。
- 假如一个packet在发送后,在收到DN返回的ack确认消息时超时,传输中止,将此时所有DN中正在传输的packet剔除,并进行回滚。
- 重新选择DN节点(剔除坏的DN节点,包括里面的所有信息)内部重新建立pipeline建立完成之后,继续传输(客户端不需要重新跟NN交互获取新的DN列表)
- 只要有一个DN节点收到了数据,DN上报NN已经收完此块,NN就认为当前block已经传输成功!
- NN会自动维护副本数!
2,读取文件时DN挂了
DataNode 挂了只需要失败转移到其他副本所在的 DataNode 继续读取
3.读取到的文件数据损坏
- 读取到的文件数据块若校验失败可认定为损坏,依然可以转移到读取其他完好的副本
- 并向 NameNode 汇报该文件 block 损坏,后续处理由 NameNode 通知 DataNode 删除损坏文件 block,并根据完好的副本来复制一份新的文件 block 副本。
5.4 hdfs不擅长处理哪些场景?
hdfs擅长处理大数据规模场景下的数据处理场景,常规的接口响应是秒级(几秒到十几秒),数据处理、清晰等场景下,相对于数据的处理和聚合运算,这些时间相对客户忽略不计。有些场景不适用于hdfs集群,总结如下
- http请求加载图片等: http请求的接口响应是毫秒级别,hdfs是秒级,相差比较大,响应时间相对较长,事实上很多http请求都配置了超时时间(如设置为3s),会导致图片经常加载不成功。
- 小文件、大量请求: 小文件会占用NN的大量内存,同时由于NN只有1个节点在响应相关的接口,因此NN很容易成为瓶颈。如果NN的内存heap配置过小,容易oom;如果配置过大,会导致长GC,同时节点异常时的恢复时间比较长;
5.5 单个节点的不同磁盘使用率差别很大,怎么做数据均衡?
当单个节点的多个磁盘使用率差异很大时,希望进行数据均匀。
hdfs diskbalancer -plan <datanode_hostname>
hdfs diskbalancer -execute 'plan_json_path'
hdfs diskbalancer -query <datanode_hostname>
5.6 各个节点的磁盘使用率差别很大,怎么做数据均衡?
可以使用均衡器 hdfs balancer
,阈值默认是10%
threshold参数表示每个DataNode的HDFS使用率于集群的平均DFS利用率的偏差百分比。以任意一方式(更高或更低)超过该阈值将意味着该节点会被重新均衡。 如下面的案例所示,可以运行不带任何参数的balancer命令,则此均衡器明朗了使用10%的默认阈值,这意味着均衡器通过将块从过度使用的节点移动到未充分使用的节点来均衡数据,直到每个Datanode的磁盘使用率不超过集群中平均磁盘使用率的正负10%。
有时,可能希望将阈值设置为不同的级别,例如,当集群中的可用空间变小,并且你希望将单个DataNode上使用的存储量保持在比默认的10%阈值更小的范围内时,可以这样指定阈值"hdfs balancer -threshold 5"
当运行均衡器时,它会查看集群中的两个关键HDFS使用情况值:
- 平均DFS使用百分比: 可以通过计算得到集群中使用的平均DFS百分比:“Average DFS Used = (DFS Used * 100) / Present Capacity”
- 节点使用的DFS百分比: 此度量显示每个节点使用的DFS百分比。
如
hdfs balancer -threshold 50
通常需要匹配均衡器的限制带宽
# 限制100mb/s
hdfs dfsadmin -setBalancerBandwidth 104857600
说明
- 默认的DataNode策略是在DataNode级别均衡存储,但均衡器不会在DataNode的各个存储卷之间均衡数据。
- 仅当DataNode使用的DFS百分比和(由集群使用的)平均DFS之间的差大于(或小于)规定阈值时,均衡器才会均衡DataNode。否则,它不会重新均衡集群。
- 均衡器运行多长时间取决于集群的大小和数据的不平衡程度。第一次运行均衡器,或者不经常调度均衡器,以及在添加一组DataNode之后运行均衡器,它将运行很长时间(通常是几天,如果数据量达到PB或者接近EB级别,可能需要一个多月的时间来均衡哟~)
- 如果有一个数据写入和删除频繁的集群,集群可能永远不会达到完全均衡的状态,均衡器仅仅将数据从一个节点移动到另一个节点。
- 向集群添加新节点后最好立即运行均衡器。如果一次添加大量节点,则运行均衡器需要一段时间才能完成其工作。
- 如果确定阈值?这很容易,秩序选择整个集群中节点最低DFS使用百分比即可。不必花费大量的时间了解每个节点使用的DFS百分比,使用
hdfs dfsadmin -report
命令即可找出正确的阈值。阈值越小,均衡器需要执行的工作越多,集群就越均衡。
配套的优化配置,如下配置不能热加载,需要重启相关的datanode进程才能生效
# 移动的线程数,默认值是1
<property>
<name>dfs.datanode.balance.moverThreads</name>
<value>4096</value>
</property>
# 移动的网络带宽限制,默认是10 MBytes/s,当前配置是100 MBytes/s
<property>
<name>dfs.datanode.balance.bandwidthPerSec</name>
<value>104857600</value>
</property>
# 指定数据节点之间进行数据平衡操作时允许的最大并发移动任务数, 默认值是5
<property>
<name>dfs.datanode.balance.max.concurrent.moves</name>
<value>50</value>
</property>
5.7 如何配置单个节点保留磁盘大小?
<property>
<name>dfs.datanode.du.reserved</name>
<value>10737418240</value>
</property>
dfs.datanode.du.reserved的单位为字节,该参数配置是针对单个磁盘,上面的预留空间为10G,预留30G:32212254720
预留空间可能遇到的问题:预留了10G,但是该节点磁盘空间去没有真正剩余,而是一直会放数据,原因可能是如下方面:
通过 df -h 查看容量显示如下:
Filesystem Size Used Avail Use% Mounted on
/dev/sda4 243G 200G 31G 87% /data
注:说明总容量为243G,使用了200G,可用31G 这里有个问题,使用量+可用量!=总容量,少掉了12G,这就是问题所在。
dfs.datanode.du.reserved设置为10G的话
通过hadoop dfsadmin -report
查看显示如下
Configured Capacity: 228260941824 (232.58 GB)
DFS Used: 208414818078 (194.10 GB)
Non DFS Used: 0 (0 B)
DFS Remaining: 19846123746 (38.48 GB)
Configured Capacity显示的dfs.data.dir指定的目录空间总容量大小-dfs.datanode.du.reserved的容量
,
如果像之前设置 dfs.datanode.du.reserved为10G的话,Configured Capacity为:232.58 GB,DFS Remaining则为38.48G,
但实际/dev/sda4 可用空间才只有31G,所以有数据的话会一直会存入该节点,直到放满。
解决方法:将dfs.datanode.du.reserved设置更大。目前设置为30G
这样,hadoop dfsadmin -report 查看
Configured Capacity: 228260941824 (212.58 GB)
DFS Used: 208414818078 (194.10 GB)
Non DFS Used: 0 (0 B)
DFS Remaining: 19846123746 (18.48 GB)
dfs可用空间18.48<31G,所以当dfs全部用完,磁盘/dev/sda4还是有13G空间空闲,达到要的效果!
所以,需要注意的是,DFS Remaining 应该小于 预留的磁盘容量,才能保证磁盘空间