基本概念
namenode和Secondary NameNode
NameNode主要是用来保存HDFS的元数据信息,比如命名空间信息,块信息等。当它运行的时候,这些信息是存在内存中的。但是这些信息也可以持久化到磁盘上。
NN有两个不同的文件:
- fsimage - 它是在NameNode启动时对整个文件系统的快照
- edit logs - 它是在NameNode启动后,对文件系统的改动序列
只有在NameNode重启时,edit logs才会合并到fsimage文件中,从而得到一个文件系统的最新快照。但是在产品集群中NameNode是很少重启的,这也意味着当NameNode运行了很长时间后,edit logs文件会变得很大。在这种情况下就会出现下面一些问题:
- edit logs文件会变的很大,怎么去管理这个文件是一个挑战。
- NameNode的重启会花费很长时间,因为有很多改动要合并到fsimage文件上。
- 如果NameNode挂掉了,那我们就丢失了很多改动因为此时的fsimage文件非常旧。
因此为了克服这个问题,我们需要一个易于管理的机制来帮助我们减小edit logs文件的大小和得到一个最新的fsimage文件,这样也会减小在NameNode上的压力。
SecondaryNameNode就是来帮助解决上述问题的,它的职责是合并NameNode的edit logs到fsimage文件中。
看看Secondary NameNode是怎么工作的。
- 首先,它定时到NameNode去获取edit logs,并更新到自己的fsimage上。
- 一旦它有了新的fsimage文件,它将其拷贝回NameNode中。
- NameNode在下次重启时会使用这个新的fsimage文件,从而减少重启的时间。
Secondary NameNode的整个目的是在HDFS中提供一个检查点。它只是NameNode的一个助手节点。这也是它在社区内被认为是检查点节点的原因。
namenode的单点问题
很显然,namenode有2个问题:
- 单点
- 受制于内存限制,不好做水平扩展
分布式集群部署
安装
安装脚本:
# 控制机为NN所在机器,通过ansible操作其他节点
# install hadoop cluster env, control machine is hadoop-master where namenode resides
# you SHOULD install ansible first, command: pip3 install ansible
# install openjdk
ansible all -m command -a 'yum install -y java-1.8.0-openjdk-devel.aarch64'
# modify /etc/profile env-variables
ansible all -m script -a '/opt/hadoop/install_hdfs.sh'
# copy hadoop package
ansible all -m command -a 'mkdir -p /opt/hadoop'
ansible all -m command -a 'rm -rf /opt/hadoop/hadoop-2.10.0'
ansible all -m copy -a "src=/opt/hadoop/hadoop-2.10.0.tar.gz dest=/opt/hadoop"
# extract tar.gz
ansible all -m command -a 'tar -zxvf /opt/hadoop/hadoop-2.10.0.tar.gz -C /opt/hadoop >null'
# hadoop的几个核心文件:core-site.xml、hadoop-env.sh、hdfs-site.xml和slaves文件
# 其中slaves文件是master节点特有的,DN节点不需要!
# substitute cfg files
ansible all -m copy -a "src=/opt/hadoop/hadoop-2.10.0/etc/hadoop/core-site.xml dest=/opt/hadoop/hadoop-2.10.0/etc/hadoop/"
ansible all -m copy -a "src=/opt/hadoop/hadoop-2.10.0/etc/hadoop/hadoop-env.sh dest=/opt/hadoop/hadoop-2.10.0/etc/hadoop/"
ansible all -m copy -a "src=/opt/hadoop/hadoop-2.10.0/etc/hadoop/hdfs-site.xml dest=/opt/hadoop/hadoop-2.10.0/etc/hadoop/"
# datanodes DONT need slaves file
ansible all -m command -a 'rm -rf /opt/hadoop/hadoop-2.10.0/etc/hadoop/slaves'
install_hdfs.sh设置环境变量,其内容为:
echo 'export JAVA_HOME=/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.181.b15-5.h4.eulerosv2r8.aarch64' >>/etc/profile
echo 'export JRE_HOME=${JAVA_HOME}/jre' >>/etc/profile
echo 'export HADOOP_HOME=/opt/hadoop/hadoop-2.10.0' >>/etc/profile
echo 'export HADOOP_CONF_DIR=${HADOOP_HOME}/etc/hadoop' >>/etc/profile
echo 'export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib' >>/etc/profile
echo 'export PATH=${JAVA_HOME}/bin:${HADOOP_HOME}/bin:${HADOOP_HOME}/sbin:$PATH' >>/etc/profile
echo 'export HADOOP_OPTS="-Djava.library.path=${HADOOP_HOME}/lib/native"' >>/etc/profile
特别说明,secondary NN要单独部署,只需在hdfs-site.xml里加入:
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>xx.xx.xx.xx:50090</value>
</property>
hadoop启动脚本就会自动识别出secondary NN。
启动
首先,在master节点格式化NN:
hdfs namenode -format
这个动作只做一次就行了。
接着,启动dfs:
start-dfs.sh
顺带说一下,start-dfs脚本会自动识别出secondary NN。
可以到master和slave上用jps命令查看进程。但进程存在也不意味着集群一定是ok的,可以在NN上执行:
hdfs dfsadmin -report
查看集群配置,如果输出像这样,各项都为0:
Configured Capacity: 0 (0 B)
Present Capacity: 0 (0 B)
DFS Remaining: 0 (0 B)
DFS Used: 0 (0 B)
DFS Used%: 0.00%
Under replicated blocks: 0
Blocks with corrupt replicas: 0
Missing blocks: 0
Missing blocks (with replication factor 1): 0
Pending deletion blocks: 0
说明集群是有问题的。
最后,为了让webhdfs可以使用,我们必须在所有DN上配置/etc/hosts:
127.0.0.1 xx.xx.xx.xx localhost localhost.localdomain localhost4 localhost4.localdomain4
使用说明
配置项
core-site.xml是全局配置,hdfs-site.xml和mapred-site.xml分别是hdfs和mapred的局部配置。
io.file.buffer.size=4096
在读写文件时使用的缓存大小。这个大小应该是内存Page的倍数。建议1M
dfs.blocksize
块大小,2.10版本默认是128M
HDFS端口
参数 | 描述 | 默认 | 配置文件 | 例子值 |
---|---|---|---|---|
fs.default.name | namenode RPC交互端口 | 8020 | core-site.xml | hdfs://master:8020/ |
dfs.http.address | NameNode web管理端口 | 50070 | hdfs-site.xml | 0.0.0.0:50070 |
dfs.datanode.address | datanode 控制端口 | 50010 | hdfs-site.xml | 0.0.0.0:50010 |
dfs.datanode.ipc.address | datanode的RPC服务器地址和端口 | 50020 | hdfs-site.xml | 0.0.0.0:50020 |
dfs.datanode.http.address | datanode的HTTP服务器和端口 | 50075 | hdfs-site.xml | 0.0.0.0:50075 |
副本数
默认副本数是3。
注意
:修改了副本数(dfs.replications),对已经上传了的文件不会起作用。
每个datanode上只能存放一个副本 ,所以如果你只有3个datanode,但是你却指定副本数为4,是不会生效的 。
副本的代价:
Replication is expensive – the default 3x replication scheme in HDFS has 200% overhead in storage space and other resources (e.g., network bandwidth). However, for warm and cold datasets with relatively low I/O activities, additional block replicas are rarely accessed during normal operations, but still consume the same amount of resources as the first replica.
所以hadoop3搞了一个EC技术来区分warm和cold数据。
dfs.permissions
开启文件访问权限的检查,这个检查跟linux系统的文件权限检查类似,即user/group/other的rwx权限。跟linux相同的是,root用户能删除所有文件。
该选项默认是false,建议改为true,修改后要重启hdfs才能生效。
hdfs的用户和用户组
HDFS本身的组和用户都是同步Linux系统的,可以切换到某个用户,用hadoop命令行访问hdfs。
如果是从web上访问,使用的是hadoop.http.staticuser.user参数指定的用户,默认是dr.who。
块大小设置
为什么HDFS中块不能设置太大,也不能设置太小?
- 如果块设置过大,
一方面,从磁盘传输数据的时间会明显大于寻址时间,导致程序在处理这块数据时,变得非常慢;
另一方面,mapreduce中的map任务通常一次只处理一个块中的数据,如果块过大运行速度也会很慢。
- 如果块设置过小,
一方面存放大量小文件会占用NameNode中大量内存来存储元数据,而NameNode的内存是有限的,不可取;
另一方面文件块过小,寻址时间增大,导致程序一直在找block的开始位置。
因而,块适当设置大一些,减少寻址时间,那么传输一个由多个块组成的文件的时间主要取决于磁盘的传输速率。
HDFS中块的大小为什么设置为128M?
- HDFS中平均寻址时间大概为10ms;
- 经过前人的大量测试发现,寻址时间为传输时间的1%时,为最佳状态;
所以最佳传输时间为10ms/0.01=1s
- 目前磁盘的传输速率普遍为100MB/s;
计算出最佳block大小:100MB/s x 1s = 100MB
所以我们设定block大小为128MB。
ps:实际在工业生产中,磁盘传输速率为200MB/s时,一般设定block大小为256MB
磁盘传输速率为400MB/s时,一般设定block大小为512MB
secondary namenode独立部署
secondary namenode的内存需求跟nn相同,所以,一般部署在独立的机器上。
Secondary NN独立部署只需往hdfs-site.xml里加入这一段即可:
<property>
<name>dfs.namenode.secondary.http-address</name>
<value>xx.xx.xx.xx:50090</value>
</property>
如果NN故障(比如断电前没关闭hadoop),可用secondary NN的数据来恢复NN,secondary NN的fsimage数据存放在
${hadoop.tmp.dir}/dfs/namesecondary/
动态新增DN
1、修改 master节点上的hadoop配置文件etc/hadoop/slaves 文件,添加新增的节点 slave3
2、在新增的 slave3 节点执行命令 hadoop-daemon.sh start datanode 启动 datanode
3、配置/etc/hosts为
127.0.0.1 ${实际ip} localhost localhost.localdomain localhost4 localhost4.localdomain4
以保证webhdfs可用。
4、最后查看http://xx.xx.xx.xx:50070/dfshealth.html#tab-datanode,看DN是否成功加上
OK,新增的节点 slave3 启动了 DataNode,jps命令可以查看。 实现了动态向集群添加了节点,刷新即可看到。
常见问题
namenode能支持多少文件?
一个文件在namenode的内存里占用300字节,假设namenode有128G内存,则总共可支持:
128G/300字节=458,129,844
4亿多个文件,假设一个case有30个文件,则一共可存放15,270,994个case。如果一个case压缩成一个tar包,namenode存放的文件数量还可以继续上升。
Namenode consumes about 150 bytes for block metadata storage and 150 bytes for file metadata storage. So let's assume that your cluster block size is 128Mb and each of your 100 file is around 100Mb size. Then each file consumes 300 bytes of memory in namenode. Name node will be consuming 300*100=30000bytes of data.
hdfs中一个文件占一个块吗?
答案是:是,这是相关说明:
HDFS stores small files inefficiently, since each file is stored in a block
但不要以为一个128M的块会被一个1M的文件占满,物理上这个块的大小还是1M:
a file in HDFS that is smaller than a single block does not occupy a full block’s worth of underlying storage
所以块大小128M只是一个上限值。
那为啥hdfs存小文件很低效呢,主要还是namenode内存的问题:
block metadata is held in memory by the namenode. Thus, a large number of small files can eat up a lot of memory on the namenode. (Note, however, that small files do not take up any more disk space than is required to store the raw contents of the file. For example, a 1 MB file stored with a block size of 128 MB uses 1 MB of disk space, not 128 MB.)
访问webhdfs时,datanode地址始终返回localhost:50075
webhdfs跟httpfs不同,它会直接把datanode的http地址返回给客户端,然后客户端跟datanode连接,获取数据。
实际中,从windows上用python-hdfs库访问hadoop,遇到的问题是:每次datanode返回的ip地址是localhost,报错信息如下:
requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=50075): Max retries exceeded with url: /webhdfs/v1/test1/pp.txt?op=OPEN&namenoderpcaddress=xx.xx.xx.xx:9000&offset=0
(Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x00000141F9D07438>: Failed to establish a new connection: [WinError 10061] 由于目标计算机积极拒绝,无法连接。')
我们在windows上用telnet连接hdfs的50070和50075端口,都没有问题,可见这两个端口确实开放了,应该是程序或配置的问题,导致datanode的host name返回错了。
google之,发现可能是/etc/hosts的问题,如果我们把/etc/hosts改为:
127.0.0.1 datanode-1 localhost
返回错误变成了:
requests.exceptions.ConnectionError: HTTPConnectionPool(host='datanode-1', port=50075)
所以,我猜测hadoop伪分布式模式下,webhdfs拿到了127.0.0.1,然后到/etc/hosts里查一下主机名,再返回给客户端。为此,我们这样修改/etc/hosts:
127.0.0.1 xx.xx.xx.xx localhost localhost.localdomain localhost4 localhost4.localdomain4
让webhdfs把127.0.0.1换成真实ip,果然,问题得到了解决。
说明
:该问题解决后,hadoop web UI上无法上传文件的问题也同时解决!
hdfs作为备份仓库
关系数据库导入hdfs
使用sqoop工具
HDFS监控
Ambari和CDH
OZone vs hdfs
Ozone 是 Apache Hadoop 社区推出的面向大数据领域的新一代分布式存储系统,它的推出解决了 Hadoop 分布式文件系统在可扩展性上限方面的缺陷问题,能够支持百亿级以上文件规模的存储。
看了一下,Ozone似乎是将NN的内存信息转储到k-v数据库(RocksDB)里,这样NN扩展受制于机器内存的问题应该能解决,但是查询效率肯定会有所下降,需要有一些取舍和折中。
RocksDB
RocksDB是基于Google的开源key value存储库LevelDB,功能非常强大,强大到可以作为mysql的底层存储引擎,对底层存储做了很多的优化。
rocksdb原理分析文章