1.fsimage加载阶段,主要耗时的操作:
1.1)FSDirectory.addToParent(),功能是根据路径path生成节点INode,并加入目录树中,占加载时间的73%;
1.2)FSImage.readString和PermissionStatus.read操作是从fsimage的文件流中读取数据(分别是读取String和short)的操作,分别占加载时间的15%和8%;
优化方案:对fsimage的持久化操作采用多线程技术,为目录列表中的每个目录存储开辟一个线程,用来存储fsimage文件。主线程等待所有存储的子线程完毕后完成对fsimage加载。这样,存储时间将取决于存储最慢的那个线程,达到了提高fsimage加载速度的目的,从而在一定程度上提升了NameNode启动速度。
2.blockReport阶段,DataNode在启动时通过RPC调用NameNode.blockReport()方法, 主要耗时操作:
2.1)FSNamesystem.addStoredBlock调用,对每一个汇报上来的block,将其于汇报上来的datanode的对应关系初始化到namenode内存中的BlocksMap表中,占用时间比为77%;此方法中主要耗时的两个阶段分别是FSNamesystem.countNode和DatanodeDescriptor.addBlock,后者是java中的插表操作,而FSNamesystem.countNode调用的目的是为了统计在BlocksMap中,每一个block对应的各副本中,有几个是live状态,几个是decommission状态,几个是Corrupt状态。
2.2)DatanodeDescriptor.reportDiff,主要是为了将该datanode汇报上来的blocks跟namenode内存中的BlocksMap中进行对比,以决定那个哪些是需要添加到BlocksMap中的block,哪些是需要添加到toRemove队列中的block,以及哪些是添加到toValidate队列中的block,占用时间比为20%;
3.锁的竞争成为性能瓶颈
优化方案:其中锁内耗时的操作有FSEditLog.logXXX方法,可以考虑将FSEditLog的logXXX操作放到写锁外记录,但会引起记录的顺序不一致,可以通过在写锁内生成SerialNumber,在锁外按顺序输出来解决;
4.UTF8/Unicode转码优化成为性能瓶颈
优化方案:用SIMD指令的JVM Intrinsic转码实现,32bytes比目前Hadoop内pure Java实现快4倍,64bytes快十几倍。
5.RPC框架中,N个Reader将封装的Call对象放入一个BlockingQueue,N个Handler从一个BlockingQueue中提取Call对象,使得此BlockingQueue成为性能瓶颈
优化方案:采用多队列,一个Handler一个BlockingQueue,按callid分配队列。
6.sendHeartbeat阶段,在DataNode调用namenode.sendHeartbeat()方法时会DF和DU两个类,它们通过Shell类的runComamnd方法来执行系统命令,以获取当前目录的 df, du 值,在runComamnd方法中实质是通过java.lang.ProcessBuilder类来执行系统命令的。该类由JVM通过Linux 内核来fork 子进程,子进程当然会完全继承父进程的所有内存句柄,jstack看到JVM此时线程状态大部分处于WAITING, 这个过程会影响DFSClient写入超时,或关闭流出错。
阅读(1106) | 评论(0) | 转发(0) |