No.38期
平均数计算
Mr. 王:再来看一个例子——均数计算。我希望借助这个例子,仔细讲解一下关于combiner 的问题。
小可:从前面的例子可以看出,其实 combiner 和 Reducer 挺像的,它们做的都是合并工作。
Mr. 王:没错。它们的确有很多相似之处。
小可:那直接把 Reducer 拿出来做 combiner 就好了啊。
Mr. 王:有的时候的确可以这样实现,但是绝大多数时候不行。至于为什么不行,我会在后面告诉你。
但是需要记住的一点是, combiner 是一个可选的优化,不论有没有 combiner,程序都必须能正确地运行出结果;而 combiner 的出现,只是提高了系统运行效率。
combiner 可能运行,也可能不运行,还可能会运行多次,这与具体的数据项构成有关。
好,回到例子上,这个例子是找到与相同键值相关联的所有整数的平均数。 Mr. 王转身拿出了白板,把算法写在了上面。
Mr. 王:这个是版本 1。你来解释一下这个算法的工作过程。
小可指着白板,说:这个 Mapper 几乎什么都没做啊,遇到一个字符串和整数对,就将其传递给 Reducer。
至于 Reducer,它是根据字符串进行匹配的,将具有相同键值的字符串以及对应的整数值收集到一起,然后剩下的部分就是对这些值求平均数, sum 累计所有的整数 r, cnt对其出现的 r 的数量进行计数,最后返回它。
Mr. 王:想一想,这里的 Reducer 能不能用来做 combiner ?
小可想了想,说:这里的 Reducer 做的就是平均数计算,如果把它用作 combiner 的话,中间就会产生很多只带有平均数值的结果。
Mr. 王:用这样的结果,能求出最终的平均数吗?
小可:平均数的算术平均数不是所有值的平均数,所以结果不对。
Mr. 王:好,那我们来看看版本 2。
小可:这个版本的 combiner 携带了每个平均数的 count,我们可以通过这个 count 来还原每一组平均数的总数,最后通过 count 的和与每一组平均数的和来求出所有数据的平均数,这样就能在 Reducer 中求解出总的平均数了。
这个版本是比较不错的。
Mr. 王:此言差矣,这个版本是不能用的。
小可一脸惊讶地说:这是为什么呢?看起来是一种很不错的设计啊。
Mr. 王:想一想, combiner 对于程序来说是不是一个必要的环节?
小可:不是, combiner 有助于优化效率,但是去掉它也不影响 MapReduce 的运行。
Mr. 王:那么在这个版本里面呢?
小可恍然大悟,说:哦,这个版本的确有问题, combiner 不仅进行了优化,而且还改变了输入输出数据类型,如果在这里去掉 combiner 的话,那么 Mapper 函数的输出数据类型与Reducer 函数的输入数据类型就不匹配了。
Mr. 王:在设计和编写程序时一定要细心。这里我们在 Mapper里面稍作修改,请见版本 3。
Mr. 王:想想看,还可以怎么改?
小可:嗯,这次即使把 combiner 彻底去掉,也不会影响整个程序的运行结果,只是在Mapper 上面稍作修改,效果还是很好的。
Mr. 王:想想看,还可以怎么改?
小可:前面提到过一种设计思想叫作 In-Mapper,对于这种需要额外定义 combiner 的,可以试试吧。
Mr. 王:很好,我们给出版本 4。
这个版本把 combiner 做的事情全部集成到了 Mapper 中,使得 combine 操作在执行 Map 函数时就做到了,进一步减小了程序的通信复杂度。
内容来源:灯塔大数据