百PB级Hadoop集群存储空间治理

秦夏 2019-04-26 10:23:43

现在这个世道,随便什么公司什么人都张嘴闭嘴大数据,连做个几十人的问卷都敢叫大数据调查分析。真是无知者无畏。

 

但也真有不少公司是真的有足够大的数据量的,也确实是在用心做大数据。这些公司通常规模不小,但盈利不一定理想。就算能稳定盈利,也一定有不小的成本压力。因为,大数据如果真的够大,是真的很费钱。

 

以我所在的公司为例,每年的服务器采购成本就已经好几千万,眼看奔着8位数去了。

 

因此我们有很强的节省成本的动力。

 

另一方面,之前我在思考作为公共部门和基础设施部门,在不做业务不赚钱的情况下,怎么体现自己的价值。其中很重要的一点就是,省钱就是赚钱呀,体现在公司收支上效果是差不多的。

 

在计算资源可复用、可灵活调度的情况下,存储空间往往是带来成本的最重要的原因。这篇文章就简单梳理下这几年我们在数十 PB 到百 PB 级别数据量下对存储空间做的一些治理工作。

 

一、降低备份数

 

大家都知道 HDFS 是靠着 3 副本来保证数据的高可用的。但也正是这 3 副本带来了 3 倍的成本。那要降低成本很自然的就想到降低副本数。

 

这个办法看起来很笨也很 low,不过确实能解决问题。当然考虑到会牺牲一定高可用性的风险,确实也不是个普适性的办法。

 

我们把这个办法用在临时文件上,或者说是线上业务不会直接用到的数据上。就算真的丢了,也不会直接影响到业务。要么确实没用,丢了就丢了,要么能从其他数据恢复过来。

 

由于我们对 Hive 库做了比较严格的权限管理,但又为了给大家留一定的灵活空间来开发调试和做实验,非线上的业务都被赶到了 tmp 库。虽然我们设定了定时删除的策略,但 tmp 库的存储开销仍然稳定在一个比较高的水平。

 

于是我们写了这么个脚本,定时遍历去把 tmp 库的文件副本数设为 2。这样就把 tmp 库的存储消耗降低了 1/3。这可就是几百万的 RMB。

 

当然也考虑过修改 Hadoop 的源码,自动在分配 block 的时候就去把这个事做了,而不是事后再去改副本数。简单讨论了下,觉得一个小脚本就能解决的问题,事后再做代价也不大,没必要去侵入代码增加复杂性。

 

另外值得一提的是,在节点数足够多而网络带宽也足够大的情况下,如果存储压力实在大,其实可以考虑把更多的数据设置为 2 副本。因为即使有一台机器挂了,也能很快从其他机器上通过网络补回 2 副本。当然风险也是有的,如果运气差到家了,2 个副本所在的机器同时都报废了,那就真丢数据了。

 

二、压缩

 

除了删数据和减少副本外,另一个很容易想到的办法就是压缩。

 

 

上面的图列出了 Hadoop 最常见的几种压缩格式。其中 native 决定了对单个文件的处理性能,毕竟 Java 在这种计算密集型的活上还是比不过 C 系列的。而 splitable 决定了一个文件是否可切分给多个 mapper 处理,也就是文件是否能被并行处理,同样也会对性能造成很大影响。

 

所以从定性的角度考虑,单看性能,lzo 和 bzip2 似乎是首选。

 

但性能到底怎么样,还得看实际的性能测试结果,由于时间实在太久,一时找不到当时的数据。从网上找了个 benchmark 看看。不要纠结绝对数字,只要知道相对差距就行。

 

很明显,bzip2 压缩和解压速率实在太慢了,差了数量级了,第一个被淘汰。

 

剩下3个,gzip 压缩比最高,也就是最省空间,但处理速率相对慢些,但也不至于像 bzip2 那么夸张。lzo 和 snappy 无论压缩比还是处理速度,都很不错,再考虑到 splitable,似乎 lzo 应该是首选。

 

但实际上,lzo 有个不可忽视的特性。lzo 的 splitable 是需要额外的索引文件来支持的,每个文件都需要有一个同名的索引文件。并且这个索引文件需要单独去生成。这还不算,索引文件会导致实际文件数多出一倍,这对于大规模集群的 NameNode 会造成巨大的压力。

 

综合上面这些情况,实际生产环境,我们采用的是这样的方式:

 

  • 原始日志采集落地的时候使用 snappy 压缩,兼顾存储空间和处理速度。

  • 周期性的对清洗完的日志文件做 archive,并把 snappy 文件转换为 gzip,以节省空间。

  • 对结构化的数据,主要是 Hive 表,采用 parquet+gzip 的方式,gzip 节省空间,而相对于 snappy 的性能劣势,则由 parquet 的性能优势来弥补。

 

这样,就能在存储空间和性能之间找到比较好的平衡。

 

三、冷热分层

 

在存储领域有个很流行的词,叫异构存储(heterogeneous storage),大白话讲就是不同类型的存储放在一个系统里,比如 RAM、SSD、DISK 等等。不少类似 Spark 这样的框架都对异构存储做了广泛的支持。

 

异构存储通常用来解决访问性能问题,这很容易理解,不同的存储介质访问速度普遍差了数量级。但同时,空间大小和成本也差了数量级,因此也能被用来节省成本。

 

HDFS 定义了两个概念来支持异构存储。

 

第一个概念:Storage Type

 

用来表示不同类型的存储,包括:

 

  • ARCHIVE,其实就是更大更便宜的硬盘,花同样多的 RMB 能存下更多的数据。我们生产环境单台 128 TB。

  • DISK,常见的普通硬盘,我们生产环境单台空间 48TB。

  • SSD,常见的固态硬盘。

  • RAM_DISK,其实就是内存,一般不会这么奢侈。

 

很显然,从上到下越来越快但也越来越贵。

 

第二个概念:Storage Policy

 

用来表示不同的存储策略,可以对应数据的冷热程度,也就是使用频次。包括:

 

  • Hot,热数据,经常被访问到的数据,所有副本都保存到 DISK

  • Cold,冷数据,很少访问的数据,所有副本都保存到 ARCHIVE

  • Warm,温数据,介于冷热之间的数据,一个副本保存在 DISK,其他全部在 ARCHIVE

  • All_SSD,没有冷热对应,所有副本保存在 SSD

  • One_SSD,没有冷热对应,一个副本保存在 SSD,其他都在 DISK

 

不同版本对以上两个概念的支持可能略有差异。既然是要节省成本,那 SSD 自然就排除掉,离线大数据处理的场景也确实不太有需要 SSD 的情况。

 

通常按这个思路去划分数据冷热,然后设置 Storage Policy 做就能解决大部分问题了。至于怎样定义和衡量数据冷热,就又是一个可以另开一篇的话题了。简单提点思路,可以按照数据时间和访问次数两个维度去划分区间,从 HDFS 审计日志统计结果。

 

除了社区的默认支持外,我们在 hot warm cold 的基础上,又加了一层 frozen 层,用来保存最冷的数据。

 

考虑到 ARCHIVE 已经是最便宜的存储介质了,具体 frozen 的效果并没有也没办法再在 Storage Type 上做文章。我们把目光转移到了第一节提到的降低备份数上。

 

当然不能是简单的设置 repica,不然这部分就直接放第一节讲了。我们使用的是 HDFS 的纠删码(erasure code)。

 

通俗点说就是 HDFS 上的 RAID。RAID 这个思路其实早就被 Facebook 和腾讯这样的公司在生产环境大规模实践过,毕竟他们肯定是最先遇到也最有动力解决存储成本问题的公司。可惜要么版本古老不再更新维护,要么闭源没有回馈社区。

 

好在 Hadoop 3.0 正式支持了这个功能。当然,缺点也是有的。首先,代码稳定性有待考验,毕竟业界还没有大规模的 3.0 踩坑经验;其次,CDH 目前还没有发布 Hadoop 3.0 的正式版,因此部署维护就没那么方便和统一了。

 

所以,只有真的非常老和很长时间都不用的数据才适合设置为 frozen 放在启用了纠删码的 3.0 集群上。

 

按我们生产环境 archive 机器成本占 disk 机器大概 1/3 算,分层存储的空间和成本开销对比如下:

 

 

看到这个表格,相信大家都有足够的动力去做分层存储了。

 

四、大存储机器

 

但是,最近几年,有个说法开始逐渐颠覆大家的传统认知,说没有必要再分 DISK、ARCHIVE 两种机型,直接全部上大存储机器。

 

考虑到随着万兆网卡的普及,再加上网卡绑定、交换机性能的提升等,网络 IO 已经不再是瓶颈。同时考虑到数据规模,DISK/Memory 比也没有意义,因此也不用顾及计算资源相对少的问题。更何况还有相当数量的冷数据躺在哪里,根本不需要为它们预留计算资源。 

 

看起来很有道理,也值得一试。后面稍稍没那么忙了,我们会集中测试对比下性能。大家有经验的可以留言一起探讨下。

 

主要内容就是这样,其他零散的治理方法就略过了。

 

随着数据量的增长,元数据也会急剧膨胀,很快 NameNode 就会成为集群的瓶颈。解决方法是 HDFS Federation,我们在生产环境已经有了不错的实践。但这又是一个复杂的话题了,下次有机会单独开一篇再细说。

 

作者:秦夏

来源:漫谈大数据订阅号(ID:sobigdata)

dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn

活动预告