现在这个世道, 随便什么公司什么人都张嘴闭嘴大数据, 连做个几十人的问卷都敢叫大数据调查分析. 真是无知者无畏.
但也真有不少公司是真的有足够大的数据量的, 也确实是在用心做大数据. 这些公司通常规模不小, 但盈利不一定理想. 就算能稳定盈利, 也一定有不小的成本压力. 因为, 大数据, 如果真的够大, 是真的很费钱.
以我所在的公司为例, 每年的服务器采购成本就已经好几千万, 眼看奔着 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, 我们在生产环境已经有了不错的实践. 但这又是一个复杂的话题了, 下次有机会单独开一篇再细说.
来源: http://stor.51cto.com/art/201904/595654.htm