浅谈 HDFS 慢节点的解决方案

在优化 HDFS 查询性能时,慢节点问题会显著影响 SQL 的查询效率。本文浅谈了目前解决 HDFS 慢节点的几种思路。

这段时间在和客户一起优化查询慢的问题,发现大量 SQL的查询性能不达标是因为 HDFS 慢节点的问题。有时候整个 IO 性能会差到,拉 200KB 的数据能花 1分钟,这直接导致一个 OLAP 数据库的性能和 Hive 差不多。

但是有一个奇怪的现象,客户用 Presto 查并没有这个问题,只有用 StarRocks 查询才会遇到,而且整个查询耗时忽快忽慢。当客户事后去复盘慢查询的 SQL时,又基本很难复现,因为那会 HDFS 可能又不拉跨了,这让整个问题定位的过程变得非常棘手。

当然出现慢节点,我们也不能一口气甩锅给客户的环境,毕竟 Presto 不是查的好好的?

慢节点产生的原因

慢节点不是凭空产生的,别全赖用户头上,有时候也得检讨一下自己的问题。

我简单的整理了下出现慢节点的原因:

  1. 读取数据量多:比如你的谓词下推没有充分利用 ORC、Parquet 的索引,导致数据多读。那数据都读的多了,慢岂不是理所应当。
  2. IO 次数多:虽然你可能读的数据总量和 Presto 差不多,但是因为你的 IO 次数多,那么连到烂 DataNode 的概率势必也高,也容易出现慢节点。
  3. 请求的数据范围横跨多个 HDFS Block:比如你请求一个文件的 50MB~150MB 范围的数据,假设 HDFS 是按照 128MB 一个 block 划分,你这个范围的请求会涉及到两个 Block。站在应用层的角度,你以为你只发起了 1 次 IO请求,但是在 HDFS client 的源码实现上,其实你向 DataNode 发起了两次请求,每一个请求分别处理一个 Block。所以你会发现 Trino/Impala 的文件 split 切分算法,都是基于 HDFS 的 Block 来切分的,而不是按照整个文件的大小进行切分。这也是为什么 ORC、Parquet 都会让自己的 Stripe、RowGroup 都按照 HDFS 的 Block 大小进行对齐。
  4. 就是慢:当你发现读取的数据量和 Presto 差不多,IO 次数自己摸良心也觉得差不多,但还是慢。那这也真没啥办法,就是那会你运气不好,连的 DataNode 拉胯。

上面四点原因中,只有第四点是客户环境的问题,其余的都是自己的问题。下面我们讲讲如何解决“就是慢”的问题。

“就是慢”的解决办法

针对慢节点可以有两个解决办法:

Hedged Read

可以通过在 HDFS Client 客户端开启 hedged read 功能来避免慢节点。Hedged read 的原理很简单,就是当向一个 DataNode 发起的请求在规定时间内没有返回,那就向另外一个 DataNode 发起一个新请求。然后取两次请求中返回快的结果。

Hedged read 由 hdfs-site.xml 里面的 dfs.client.hedged.read.threadpool.sizedfs.client.hedged.read.threshold.millis 两个参数控制:

参数说明
dfs.client.hedged.read.threadpool.sizeHedged read 线程池大小,默认是 0,表示不开启。
你可以配置任意一个大于 0 的数字。
dfs.client.hedged.read.threshold.millisHedged read 超时阈值,当请求耗时超过这个时间,
则向一个新的 DataNode 发起一个新的请求。
Hedged Read 参数

不过你以为 hedged read 是万金油?那就大错特错了。

我在之前的文章里面《HDFS Hedged Read 的利弊分析》分析过,hedged read 因为实现上的挫,它保底要消耗多一倍的内存。期间如果超过阈值发起了 hedged read,那就是两倍。

此外你的 hedged read 线程池怎么规划呢?像现在的 OLAP 系统,IO 并发玩的都是大力出奇迹。以 StarRocks 为例,假设一台机器是 64 核,那么其 DOP = 32,然后每个 pipeline 最多有 16 个 IO 线程。也就是说 StarRocks 在单台机器上面,最多同时能发起 512 个 IO 线程。

如果你的 hedged read 线程池不够大,那些进不去线程池的 IO 请求,没有重试机会,该慢还得慢,然后成为整个查询的短板。

线程池的大小计算非常简单,hedged read 的所有请求都是在它的线程池里面进行。比如 StarRocks 同时发起了 512 个 IO 请求,那么此时 hedged read 线程池里面保底就有 512 个线程了。如果有些 IO 请求因为超过了阈值,发起了第二次请求,那么该 IO 请求在 hedged read 线程池里面则占两个位置,也就是理论上你得规划个容量为 1024 的线程池才够用。

判断线程池够不够用很简单,HDFS client 里面的 hedgedReadOpsInCurThread 指标就表示有多少个 IO 请求是没进到 hedged read 线程池的。

此外如果你第一次 IO 请求的 DataNode 慢,然后又连了一个新的 DataNode,还是慢,咋办?没办法,干等,两个矮子里面拔高个。Hedged read 最多只会向两个 DataNode 发起 IO 请求。不过可别忘了,你的 HDFS 可有三副本呢!相当于另外一个副本用不上。

IO 请求快速 timeout

这招是本次在客户那里灵验的一个方法,非常有效果。

既然你的 DataNode 慢,那我就调短和 DataNode 连接的超时时间,你要是一时半会返回不了,就立刻 timeout,然后 HDFS client 会转而向另外一个新的 DataNode 发起请求。

hdfs-site.xml 里面的dfs.client.socket-timeout 参数则控制了 HDFS client 和 DataNode 的通讯超时时间。默认值是 60000 ,单位是毫秒。这太大了,在 OLAP 这种要求秒级返回的系统里面,这个 60 秒超时是不可接受的。

那这个值设置成多少合适呢?其实 OLAP 系统一般不会涉及到大 IO 的请求,一次 IO 基本都是在 0~10MB 之间(我个人的经验之谈)。当然 Text 格式除外,因为这个就取决于你的系统实现了。不过 SR Text 格式最大单次 IO 请求也就 16MB,Trino 的 Text 则是 1MB。

所以你自己评估下,一次 IO 拉这么点数据,大概多久就够了呢?我个人觉得一般这个值设置个 5s 就差不多了。

当然你去翻看一下 Trino 的源码,你会发现它还 hard coding 了一些其他的 timeout 参数,不过在客户那里测过,没啥效果。

下面这几个参数是 Trino 额外 hard coding 的,配置在 core-site.xml 里面的参数,仅供参考。主要是控制 Client 和 NameNode,DataNode RPC 请求的参数,大家可以根据自己的心情选择性进行配置。

参数说明默认值Trino
hard
coding
ipc.client.connect.timeoutIndicates the number of milliseconds a client
will wait for the socket to establish a server
connection.
20000500
ipc.ping.intervalTimeout on waiting response from server,
in milliseconds. The client will send ping when
the interval is passed without receiving bytes,
if ipc.client.ping is set to true.
6000010000
ipc.client.connect.max.retriesIndicates the number of retries a client
will make to establish a server connection.
105
Trino Hard Coding 参数

总结

总的来说,要避免慢节点,核心还是先优化你的代码吧。至于 HDFS client 侧的 dfs.client.socket-timeout 参数,那则是解决问题的最后临门一脚。

最后揭秘一下 Presto 快的原因,因为客户在 Presto 里面配置了 hive.dfs-timeout=5s,然后这个参数其实就是等同于 hdfs-site.xml 里面的 dfs.client.socket-timeout=5000,相当于变相修改了 timeout 时间。

关于这个参数,我看了眼 Trino 的 commit 记录,一开始他们是 hard coding 了 10s,不过后面又改回了默认值 60s,具体原因见 commit 链接。

有一说一,你的坑,Trino 早就趟过了,你爸爸终究是你爸爸。每一个不起眼的配置,都是前人的智慧。

原创文章,作者:Smith,如若转载,请注明出处:https://www.inlighting.org/archives/hdfs-slow-datanode-solution

打赏 微信扫一扫 微信扫一扫
SmithSmith
上一篇 2023年11月12日 下午7:59
下一篇 2024年4月21日 下午3:41

相关推荐

  • RLE 编码在 Apache ORC 中的实现

    介绍 Apache ORC 中 RLE v1 和 RLE v2 的具体算法实现。

    2024年6月8日
    4992
  • Trino / StarRocks 阿里云 EMR Kerberos 认证指南

    Kerberos 是最为头疼的鉴权配置,但是 Hadoop 全家桶绕不开,只能硬着头皮干了。本文以 Trino 和 StarRocks 为例,讲述如何在非 EMR 的节点上,通过一…

    2023年8月21日
    1.1K0
  • HDFS Hedged Read 的利弊分析

    HDFS Hedged read 是一种优化 HDFS 客户端读取文件性能的方法。它会在存在慢节点的情况下,通过申请多个内存来提高读取性能。但是,由于 Hedged read 会频繁申请内存,可能会导致内存消耗过大,从而影响系统性能。因此,HDFS 并没有默认开启 Hedged read 功能。在使用 Hedged read 时,需要注意内存消耗的问题,以避免对系统性能造成负面影响。

    2023年11月12日
    9362

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

评论列表(3条)

  • Halle Grady
    Halle Grady 2024年5月2日 上午7:25

    you are truly a just right webmaster The site loading speed is incredible It kind of feels that youre doing any distinctive trick In addition The contents are masterwork you have done a great activity in this matter

  • dc
    dc 2024年3月26日 上午9:48

    niubi le smith