知识卡片

Hadoop大粒度对象锁Bug:底层实现细节在高并发写场景下引发连锁故障

普通读书笔记卡

内容

某音乐公司在Hadoop集群中大规模同时向HDFS写多批文件时,遇到了HDFS Client和DataNode之间出现Time Out、或HDFS Client报出”All dataNode are bad….“错误、最终导致数据写入失败的问题。深入排查后发现根因是Hadoop 2.6版本存在一个已知Bug——代码内部对某个关键对象(FsDatasetImpl)使用了粒度非常大的对象锁,在大规模并发写操作的场景下,这把粗粒度锁成为了严重的性能瓶颈甚至引发异常;这个Bug存在于2.5和2.6版本中(团队新集群用的正是2.6),已经在官方后续的2.6.1和2.7.0版本中修复,修复方案的核心思路是把这一整个大粒度的对象锁拆解成多个更细粒度的锁,同时把DataNode向NameNode发送心跳的线程从原本关联的锁中剥离出来,避免心跳这类高频、轻量的操作被这把重锁阻塞。团队最终通过不停机的平滑方式,把生产集群的Hadoop版本升级到2.7.1,之前必现的异常场景(大规模并发写多批文件)完全没有再复现,观察升级后的阻塞线程数和DataNode心跳情况都恢复正常,心跳间隔也不再随并发写入的进程数量增长而同步恶化。这个案例给出的经验是:一个开源基础设施组件出现”在高并发场景下才会暴露、常规使用场景很难复现”的诡异故障时,值得优先怀疑组件内部的并发控制实现(锁的粒度设计)是不是本身存在缺陷——这类问题的表现形式(超时、连接错误)往往和真正的根因(一把过粗的锁)在字面上看不出直接联系,需要结合”故障只在高并发写场景下出现”这个关键线索,去查阅对应版本的已知问题列表(比如Apache的JIRA issue追踪),而不是停留在应用层反复调整参数、重试逻辑去徒劳应对一个源自底层实现缺陷的问题。

参考来源

- 位置:《高可用架构(第1卷)》第6章《大数据与数据库》"6.1 某音乐公司的大数据实践"节,"6.1.3 在大数据平台重构过程中踩过的坑"(源文件:_epub-src/OEBPS/Text/Chapter6_1_4.xhtml) - 结论依据:原文说明"经过分析后我们发现,Hadoop 2.6版本存在1个Bug——代码中采用了非常大粒度的对象锁(FsDatasetImpl),在大规模写操作时导致锁异常……具体的修复方案就是将这个大粒度的对象锁分解为多个小粒度的锁……采用集群不停机的平滑方法将Hadoop生产集群的版本升级到2.7.1版本……都没有发现之前的异常信息,作业成功",直接支撑本卡片结论。 - 原始内容:经过分析后我们发现,Hadoop 2.6版本存在1个Bug——代码中采用了非常大粒度的对象锁(FsDatasetImpl),在大规模写操作时导致锁异常……具体的修复方案就是将这个大粒度的对象锁分解为多个小粒度的锁,并且将DataNDE向NameNode发送心跳线程从相关联的锁中剥离。