MapReduce作为至今仍为流行的大数据计算框架,虽然早已在各个场景下均有竞品,但是凭借大保有量和集群较高的升级成本,现在依然在行业上占有较大规模。然而,随着技术不断迭代,以及用户对数据降本越来越高的需求,即使上层计算不变,下层配套设施比如存储,比如容器都可能会往更弹性,更经济的方向演进,从而会让计算在异构环境下运行的情形越来越多。
对于大公司,一般自建iDC机房,构建自己的私有云;对于中等体量的公司,可以自建私有云,但考虑到自建机房维护成本,也可能会选择部分业务上公有云,搭建混合云环境部署大数据生态;对于小公司,如果cover不住运维成本,直接上公有云或许更合适。上公有云,又要考虑是否自建计算集群还是用各家的EMR产品,归根到底,都是由业务降本的驱动力决定的。无论是在云上自建MapReduce集群,还是使用全套EMR产品,依然需要对MR任务进行优化,以提升计算效率,或是节省资源开销,最终都能反映到成本上来。考虑到云上环境的不同,优化思路相比较传统也有所不同,接下来就会阐述。
云上环境分析
Users commonly get into trouble when their jobs input large numbers of small files, output large numbers of small files, or require random access to files. Another problem is a need for rapid access to data or for rapid turnover of jobs
这类问题在云上会展现的更加明显。
对象存储
对Mapreduce框架来说,其天生就和HDFS存在紧密联系,其运作的理念也高度利用了HDFS的特性,毫无疑问,HDFS是最适合Mapreduce的存储产品。然而,在云上环境,最常见的存储产品是对象存储,当然在云上使用HDFS自然也是可以,但是对象存储的优势就是便宜, 既然上云是为了降本,那便宜就是最大的优势。
但遗憾的是,对象存储并不太契合大数据场景,尤其是存在大量文件时,Mapreduce框架中大量文件操作在对象存储中就成了劣势。我们无法保证使用对象存储的性能保持能和HDFS保持完全一致,但是依然可以通过手段尽力去优化。
弹性容器
弹性容器的解释可以有两方面,一方面,使用云厂商按需使用的云服务器,这些云服务器的价格是浮动,而且随时会有被销毁的风险,但与之换来的是低廉的售价。因此比较适合离线业务这种存在明显波峰波谷的场景;另一方面,使用k8s做弹性调度,mapreduce的节点随时可能会回收,但是资源即用即销毁,资源的利用率得到了很大的提高。在弹性容器的场景,比较明显的特征是无法保证作业运行环境随时可用,也就是说提高mapreducce的容错能力。同时,选择合适的机型也能提高竞价实例的拉起效率和作业性能。
具体措施
配置优化
考虑到云上环境的区别,我们对MR任务的配置也需要格外注意。
- 推荐配置合适输出压缩算法。因为云上存储收费成本较高,配置合适的压缩算法所以尽可能提高压缩率是比较有效的措施。
Lz4是目前常用的,也是效果还不错的压缩算法(亲身经验比snappy效果好不少)
配置项 | 注释 |
---|---|
mapreduce.map.output.compress | 是否使用输出压缩 |
mapreduce.map.output.compress.codec | 指定特定的压缩算法 |
- 设置资源要基于集群资源和机型配比进行调整,设置资源配置的参数如下
配置项 | 注释 |
---|---|
mapreduce.input.fileinputformat.split.maxsize | 设置输入分片大小 |
mapreduce.map.memory.mb/mapreduce.reduce.memory.mb | 设置map、 reduce任务的内存 |
mapreduce.map.cpu.vcores/mapreduce.reduce.cpu.vcores | 设置map、reduce任务的vcore |
- 加大任务的重试次数,在云上容器销毁的概率相对较高,一旦因为容器重试次数达到上限导致任务失败,只会得不偿失
配置项 | 注释 |
---|---|
mapreduce.map.maxattempts | 默认4 可适当调大 |
mapreduce.reduce.maxattempts | 默认4 可适当调大 |
apreduce.reduce.failures.maxpercent | 当失败的 Map Task 失败比例超过该值,整个作业则失败,默认0 |
- 加速执行的配置
配置项 | 注释 |
---|---|
mapreduce.map.speculative/mapreduce.reduce.speculative | 开启推测执行 |
mapreduce.job.speculative.speculativecap | 推测执行功能的任务能够占总任务数量的比例 |
mapreduce.job.speculative.slownodethreshold | node级别是否适合启动某个task的speculative task的阈值 |
mapreduce.job.speculative.slowtaskthreshold | 是否可以启动speculative task的阈值 |
mapreduce.job.reduce.slowstart.completedmaps | reduce task 慢启动,当task完成比例达到某个值时即启动reducetask |
节点选型
在云上,一个相当有效但也容易导致反作用的优化措施就是更换运行节点的机型,借助k8s以及弹性伸缩器(例如autoscaler、aws karpenter等)我们可以方便基于当前资源需求快速供应所需机型(实际上并非所有机型都能自由供应,一般还有受到云厂商的供给限制),让资源使用尽可能贴合供应曲线。
然而,关于如何进行机型选型就是纯粹的经验之谈了,针对不同的情形需要对症下药,不存在公式银弹。这里我也就根据我的实际情况,总结了一些非常主观的注意点,希望能带来启发。
任务提交的vcore和内存配比要尽量和机器配比保持一致
在资源需求相对稳定时,可以尝试用更大规格机型来塞更多任务,虽然单价更贵,但台数下降,有时总成本也会更少
如果使用的按需服务器,一定要时刻关注价格变化,根据价格选择机型
深入定制
实际上由于MapReduce本身架构相对较旧,在云上尤其是非常弹性的环境下适应性确实不够好。此时如果开源的产品无法满足需求,各家云厂商也提供了他们对应的弹性大数据计算产品——当然是需要额外收费的,而且费用不低。如果不想要将自己的架构和云厂商提供的解决方案绑定,也可以基于开源去做一些定制优化,以下是我认为对性能提升帮助较大的几个切入点:
- 资源超卖。通过超卖vcore和内存带来的收益是非常明显的,尤其是当资源需求曲线不贴合供应曲线时,通过一定的超卖来做补差。但是也需要注意不同云厂商提供的云服务器本身就是容器,可能底层也一定做了超卖,需要谨慎控制超卖比例防止资源不足
- committer。在对象存储上文件的元信息修改不是原子操作,性能比hdfs要差很多,然而大数据计算中存在大量staging操作,随着小文件数量的增加,其对作业的性能影响是非常严重的。建议使用hadoop 开源的S3A committer,或者基于其思想去重写commit逻辑来优化对象存储上的不足。
- shuffle。在很多场景,上云作业往往拥有多种存储介质,不同存储介质之间可能存在较大的特性差异,这对重磁盘spill的Mapreduce计算非常不友好。推荐参考现代计算引擎例如spark,实现remote shuffle service,将存储和计算解耦,这会大大提升作业的性能和稳定性