需要深入研究Hadoop集群的存储节点并做相应的优化,减少新机型带来的性能压力。NameNode的优化可以参考(深度优化Hadoop NameNode读写性能)
1. 现状分析
为了解决上述提到的问题,需要分析Hadoop DataNode(后面简称DN)内核运行原理。我们知道DN主要用于接收用户的数据写入,并与NameNode(后面简称NN)节点定期通信,拿到执行命令。现网HA场景下,其基本的架构如下:
-
BPServiceActor会根据集群NN的配置而实例化多个,集群有3个NN则会实例化出三个NN
-
BPServiceActor主要用于从NN处获取Command(对数据的增删改查,迁移等等),上报心跳、IBR(增量块汇报)以及FBR(全量块汇报)
-
FsDataSet接口负责承接用户请求,并将对应的读写等操作落实到具体的volume上来,并对DN本省维护的一些块信息做更新操作(块大小,时间戳,完成状态等)
用户client创建文件时,会从NN上拿取待写入的DN信息。client拿到信息之后,会直接连接DN节点发起写请求,DN会根据volume的负载(一般是当前volume已使用空间),
选择负载较低的磁盘写入。写入完毕之后,会触发IBR发送给NN。当我们增加单机volume个数时,会带来如下问题挑战:
-
FsDataSet使用全局锁控制volume读写,增加volume个数无疑会增加锁竞争,且会拖慢并行写入的吞吐,降低单机的性能
-
volume增多之后,IBR以及FBR也会相应的增加,这对NN和DN通讯也会带来更大的压力
-
机器磁盘故障时,影响的数据量级比之前增加很多,需要做到快速恢复
尤其对于全局锁的使用,会给dn的读写造成比较大的影响,我们jstack追踪了一下dn的线程,可以看到线程之间blocking关系图如下,数据接收和写入线程均受到全局锁的影响:
2. 方案思考
2.1. 拆锁
针对全局锁的问题,可以考虑做拆锁方案,减少FsDataSet读写数据时引起的锁竞争与锁等待。从结构上来看,FsDataSet划分数据有两个层面上的聚合,第一个是BlockPool级别,第二个是Volume级别。对于federation集群来说,一个集群会
有>1的BlockPool。对于非federation集群来说,一个集群只有一个BlockPool。BlockPool本身会包含着多volume,结构划分如下:
对于这样的结构划分,很自然地,也会考虑拆解为BlockPool级别的(父)锁以及Volume级别的(子)锁。按照场景我们来确定一下不同的加锁规则:
-
对于一个volume内的数据读写操作,我们需要对该操作加volume级别的读写锁和BlockPool级别的读锁(优化的关键,一个volume内的数据读写,本质上没有改变BlockPool级别的元数据逻辑,因此只需要加读锁)
-
对于多个volume间的数据读写操作,例如dn内部的数据迁移,则需要加BlockPool级别的写锁,此时则不能加volume级别的锁
-
对于BlockPool级别的元数据变更,例如BlockPool清理,BlockPool添加等,这个属于BlockPool级别改动,需要加BlockPool级别的写锁。部分场景下需要加dn进程级别的全局锁
细粒度的拆锁,可以缓解竞争锁带来的额外耗时,特别是高密度场景下的dn节点,volume越多,原版本带来的锁问题越大。通过拆锁,可以实现吞吐随磁盘增加而线性增加,为用户提供更加稳定的访问。
社区版本提过类似的思想:[HDFS-15382] Split one FsDatasetImpl lock to volume grain locks. - ASF JIRA,但在branch-3版本上并未做实现,社区draft版本可能存在未解决的死锁的问题。天穹Hadoop基于这种设计理念,在hadoop-3.2版本中实现了拆锁逻辑。运行稳定之后会反馈给社区
2.2. 配置优化
高密度机型下,单机上报的block数也会超长。因此跟namenode相关交互配置,也需要做一些调整。
配置项目 | 推荐值 | 备注 |
---|---|---|
dfs.blockreport.incremental.intervalMsec | 300 | IBR延迟批量上报,写单dn的数据量会随着volume增加而加大,增加此项,可减少namenoode rpc请求压力 |
ipc.server.listen.queue.size | 2048 | 提升DN backlog配置参数,防止dn因并发连接过多导致SYNC丢包问题 |
dfs.blockreport.initialDelay | 600 | block report推迟上报的加盐参数(random delay取值范围),防止出现dn同时上报blockreport,引起block report storm |
dfs.namenode.full.block.report.lease.length.ms | 1800000 | block report租约过期时间,此租约功能主要为了解决block report storm而出现。单机block增多,需要的租约时间时间也需要相应增大 |
dfs.namenode.replication.max-streams | 500 | 高优复制流上限配置,用于降低单盘故障影响时间 |
dfs.namenode.replication.max-streams-hard-limit | 1000 | 整体复制流上限配置,用于降低单盘故障影响时间 |
dfs.namenode.replication.work.multiplier.per.iteration | 5 | 立即下发的待复制block数=存活的dn数x此配置值,用于降低单盘故障影响时间 |
3. 优化效果
发起1TB teragen高IO写入测试
1. 使用现网机型(4TB * 12磁盘,block配置为128MB),有30%左右的性能提升
2. 使用高密度机型(12TB * 36),因手上client测试机器有限,单block改为1MB来模拟并发场景,有47%左右的性能提升
通过上面的测试,也可感知到,磁盘负载越高,并发创建越多,优化性能越明显。锁的耗时主要来源于两个方面,第一个是频繁的锁申请,第二个是锁内存在的磁盘读写。并发越高,磁盘IO越高,锁耗时占比也就越高,拆锁带来的收益也越大。后面我们将发起系列测试验证新版本的稳定性和性能提升效果
4. 后续
拆锁带来的问题主要有两个,一个是死锁问题,另外一个就是并发修改问题。前者可能引起dn进入假死状态,后者可能会导致元数据混乱问题,因此,需要提前做一些操作预防和发现此类问题。针对死锁问题,需要引入锁检查,当某线程长时间持有锁时,需要有相应的日志落地。此版本也需要充分测试后,才能到现网环境中做升级。