现在这个世道,随便什么公司什么人都张嘴闭嘴大数据,连做个几十人的问卷都敢叫大数据调查分析。真是无知者无畏。
但也真有不少公司是真的有足够大的数据量的,也确实是在用心做大数据。这些公司通常规模不小,但盈利不一定理想。就算能稳定盈利,也一定有不小的成本压力。因为,大数据,如果真的够大,是真的很费钱。
以我所在的公司为例,每年的服务器采购成本就已经好几千万,眼看奔着8位数去了。
因此我们有很强的节省成本的动力。
另一方面,之前我在思考作为公共部门和基础设施部门,在不做业务不赚钱的情况下,怎么体现自己的价值。其中很重要的一点就是,省钱就是赚钱呀,体现在公司收支上效果是差不多的。
在计算资源可复用、可灵活调度的情况下,存储空间往往是带来成本的最重要的原因。这篇文章就简单梳理下这几年我们在数十 PB 到百 PB 级别数据量下对存储空间做的一些治理工作。
1、降低备份数
大家都知道 HDFS 是靠着 3 副本来保证数据的高可用的。但也正是这 3 副本带来了 3 倍的成本。那要降低成本很自然的就想到降低副本数。
这个办法看起来很笨也很 low,不过确实能解决问题。当然考虑到会牺牲一定高可用性的风险,确实也不是个普适性的办法。
我们把这个办法用在临时文件上,或者说是线上业务不会直接用到的数据上。就算真的丢了,也不会直接影响到业务。要么确实没用,丢了就丢了,要么能从其他数据恢复过来。
由于我们对 Hive 库做了比较严格的权限管理,但又为了给大家留一定的灵活空间来开发调试和做实验,非线上的业务都被赶到了 tmp 库。虽然我们设定了定时删除的策略,但 tmp 库的存储开销仍然稳定在一个比较高的水平。
于是我们写了这么个脚本,定时遍历去把 tmp 库的文件副本数设为 2。这样就把 tmp 库的存储消耗降低了 1/3。这可就是几百万的 RMB。
当然也考虑过修改 Hadoop 的源码,自动在分配 block 的时候就去把这个事做了,而不是事后再去改副本数。简单讨论了下,觉得一个小脚本就能解决的问题,事后再做代价也不大,没必要去侵入代码增加复杂性。
另外值得一提的是,在节点数足够多而网络带宽也足够大的情况下,如果存储压力实在大,其实可以考虑把更多的数据设置为 2 副本。因为即使有一台机器挂了,也能很快从其他机器上通过网络补回 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 的性能优势来弥补
这样,就能在存储空间和性能之间找到比较好的平衡。
3、冷热分层
在存储领域有个很流行的词,叫异构存储(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 算,分层存储的空间和成本开销对比如下:
看到这个表格,相信大家都有足够的动力去做分层存储了。
4、大存储机器
但是,最近几年,有个说法开始逐渐颠覆大家的传统认知。
说没有必要再分 DISK、ARCHIVE 两种机型,直接全部上大存储机器。
考虑到随着万兆网卡的普及,再加上网卡绑定、交换机性能的提升等,网络 IO 已经不再是瓶颈。
同时考虑到数据规模,DISK/Memory 比也没有意义,因此也不用顾及计算资源相对少的问题。更何况还有相当数量的冷数据躺在哪里,根本不需要为它们预留计算资源。
看起来很有道理,也值得一试。后面稍稍没那么忙了,我们会集中测试对比下性能。大家有经验的可以留言一起探讨下。
主要内容就是这样,其他零散的治理方法就略过了。
随着数据量的增长,元数据也会急剧膨胀,很快 NameNode 就会成为集群的瓶颈。解决方法是 HDFS Federation,我们在生产环境已经有了不错的实践。但这又是一个复杂的话题了,下次有机会单独开一篇再细说。