hadoop hdfs 性能调优
说明
- 其实hdfs 并不适合作为小文件的分布式存储系统 . 前人埋下的坑....
背景
- 一次线上环境的hdfs namenode 进程打满cpu. 一直在百分之6 7百 . namenode 一直在做 full gc, 此时hdfs基本处于不可用状态 , 此时系统可用内存又不足,而且cpu负载很高. 由于这个环境第一次接手维护 . 这篇文章主要总结下如何做的优化.
环境:
- hadoop 版本:Hadoop 2.6.0-cdh5.5.0
- 服务器 : CentOS release 5.6 (Final) , 32G 内存 , 8核cpu , 12块盘
- 内核版本: Linux version 2.6.18-308.13.1.el5
- hadoop 集群一共三台机器 , namenode 做了自动HA , datanode 也都在这三台机器上.
正文
这个hadoop 环境是通过CDH 的Unmanaged Deployment方式来安装的 . 可以参考这里 ,没有管理端 没有监控. (个人还是偏好社区版本 , 灵活可控. ) . 那没有监控自然是先加监控呗
指标收集
- 采集工具
没有监控首先考虑的是加个监控上去 . 由于我们线上有opentsdb , grafana . 缺了个采集工具 , 网上找了把 , 找了个tcollector可以作为指标采集的工具. 参考这里. 而且tcollector自带了hadoop 的采集脚本.这个采集脚本使用了python语言通过采集hadoop 提供的 restapi接口来实现的. 具体hadoop指标参考这里 - 兼容性的适配
例如: 1. 在hadoop_namenode.py这个采集脚本里面连接的本地是localhost地址. 由于我们hdfs没有监听localhost ip所以连不上. 2. 在procstats.py这个采集系统状态的脚本里面会用到/proc/softirqs这个系统状态文件.然而在Linux version 2.6.18这个版本里面没有这个文件. - 定制化监控
在tcollector这个程序里添加一个定制化监控还是非常简单的. 只要在它指定的文件夹中添加一个采集脚本 在程序里按照它的格式输出就可以 . 用python ,shell 实现都可以. 例如
#!/bin/bash
. "/etc/profile"
etc_path="$(cd "`dirname "$0"`"/../etc; pwd)"
run_day=$(date "+%Y-%m-%d")
time=$(date '+%H%M')
for i in `cat ${etc_path}/dir.txt` ;
do
meta_array=($(hadoop fs -count $i 2> /dev/null))
echo "hadoop.hdfs.dir.count.dirs $(date +"%s") ${meta_array[0]:-0} dir=$i "
echo "hadoop.hdfs.dir.count.files $(date +"%s") ${meta_array[1]:-0} dir=$i "
echo "hadoop.hdfs.dir.count.size $(date +"%s") ${meta_array[2]:-0} dir=$i "
done ;
这里实现的是对重点目录的增长趋势做一个监控. 输出的第一列是指标名称. 第二列是时间戳, 第三列是指标值 后面是tags键值对. tcollector在读取到脚本输出后会自动将指标输出到opentsdb中去.
- 启动tcollector
sh tcollector start -L opentsdb_host1,opentsdb_host2 -v -D
指标分析
-
下图展示了张系统的内存的指标 (其实想精确统计系统可用内存还是很困难的这个后面会提到) 从这张图看系统可用内存还是挺紧张的. 而且系统已经用了一部分swap. 我们系统的swappiness 设置的是0(可以通过命令cat /proc/sys/vm/swappiness 查看) . 这表示只有物理内存已经快用尽了才会使用swap.(ps , 好像最新的内核如果设置为0 , 表示never swap .)
系统内存
但是系统内存都用到哪里去了呢 , 统计所有进程总共占用多少内存可以用下面的命令进行统计
[root@XXX scripts]# grep Pss /proc/[1-9]*/smaps | awk '{total+=$2}; END {print total}'
结果是进程占用的内存才十多个G 远没到30多G那剩下的内存去哪里了呢,
[root@XXX scripts]# free -m
total used free shared buffers cached
Mem: 32176 32026 150 0 801 76
-/+ buffers/cache: 31148 1028
Swap: 18041 1643 16398
那系统内存被谁给用去了呢 而且此时的cpu负载一直很高
系统cpu负载 slab_info
为了看了更直观,这里的slab用了负值表示.这里可以看出系统内存波动和slab完全一致. 那slab又被谁给用去了呢
slabtop
使用slabtop命令看到slab绝大部分被ext3_inode_cache和dentry_cache 用去了. 而ext3_inode_cache是被谁用去了呢. 在top 查看进程时 ,经常看到du -sk /data1/hdfs/data/current/BP-xx.xx.xx.xx 在运行. 我们有12块盘,所以经常看到很多du -sk 在运行. 这个统计脚本的父进程是datanode, datanode 会定期(默认10分钟)统计BP的大小.我们每块盘都有大量的小文件. 其中一块盘就有差不多块200万的文件. 12块盘那就是2000多万的文件.
##统计一个目录下的文件个数
[root@XXXX hdfs]# ls -lR| grep "^-" | wc -l
1885296
这时使用du -sk是非常耗时的.它会递归文件所以会导致slab cache一直很高.基本都超过10分钟.du -sk 这个命令在2.8的版本已经可以被替换了 详情见这里
这里提到可以用df 替换du的一个方案具体实现是这样的
mv /usr/bin/du /usr/bin/du_bak
vim /usr/bin/du
#!/bin/sh
if [[ $2 == */current/BP-* ]] && [ $1 == -sk ]
then
used=`df -k $2 | grep -vE 'Used|可用' | awk '{print $3}'`
echo -e "$used\t$2"
else
echo -e "$(du_bak $@)"
fi
chmod +x /usr/bin/du
方案参考这里
由于df是基于硬盘统计的所以很快, 而du是基于文件统计的. 这种方案在datanode的目录分别在不同的硬盘上问题不大.
而且调整后的效果很明显(红框是调整后的效果)
上图可以看到slab里用到的cache明显减少 . 用于系统的cache明显增多.而且系统的cpu负载也明显降低
红框是调整后的效果
细心的同学可能还会问,为什么还是每隔6个小时slab还是会去增长呢. 这是由于datanode 的directoryscan会每隔6小时扫描一次目录这里也是递归,会去检查文件是否有坏块, 丢失块或者不一致的情况.
这里可以调整/proc/sys/vm/vfs_cache_pressure的值让操作系统更加积极的回收slab里面的cache . 默认是100 可以被调整500或1000.这里调整到500 . 下面这张图可以看到调整前后的变化.
vfs_cache_pressure调整前后比较
总结
说明
- 不同linux版本的free看到的统计方式不一样.参考这里
当我们考虑有多少cache可供回收的时候,首先要知道的是:不同版本的”free”命令计算cache值的算法不同,据不完全统计举例如下:
版本:procps-3.2.8-36
cache值等于/proc/meminfo中的”Cached”;
版本:procps-3.3.9-10.1
cache值等于/proc/meminfo的 [Cached + SReclaimable];
版本:procps-ng-3.3.10-3
cache值等于/proc/meminfo的 [Cached + Slab]。 - 系统可用内存的计算方式 参考这里
如果/proc/meminfo中有MemAvailable时取MemAvailable
/proc/meminfo中无MemAvailable时取 free+cache+ SReclaimable 当然cache 里面还有一分部可能不能被回收.
总的来说 很难精确估计系统剩余可用内存.