hive优化:让一个MR做更多的事情

来源:互联网 发布:adobe pdf 电脑软件 编辑:程序博客网 时间:2024/06/05 06:26
常常会有类似这样的需求:
数据是这样的
user_id  int      用户ID        
pay_channel int   充值渠道ID
pay_cents   int   充值金额
user_id+pay_channel为唯一键
求每个用户充值金额最多的渠道。


直观上有两种思路:
1. 先求每个用户在所有渠道上的最大充值,然后回表join
select t1.user_id , t1.pay_channel 
from table t1 join
(
    select user_id , max(pay_cents) max_pay from table group by user_id
) t2 on (t1.user_id = t2.user_id and t1.pay_cents = t2.max_pay)  ;


2.用先分析函数对pay_cent做排名标记,再用排名来过滤
select user_id , pay_channel from 
(
    select 
        user_id , pay_channel , rank(pay_cents)over)(partition by user_id ) rk
    from table 
) t1 
where rk = 1 ;




上面两个方法都需要两个MR来完成。
根据Hive的特性,我们可以有第三种思路,通过UDAF用一个MR完成。
select user_id , getKeyWithMaxValue(pay_channel , pay_cents) max_channels 
from table 
group by user_id ;


getKeyWithMaxValue(key , value)是我自定义的UDAF,用来求最大value所对应的key,这样的key可能是多个,所以返回值是list.


UDAF实现代码如下:

public class GetKeyWithMaxValue extends UDAF {      public static class GetKeyWithMaxValue_int_int implements UDAFEvaluator{        //最终结果        private HashMap<Integer , Integer > result;        //负责初始化计算函数并设置它的内部状态,result是存放最终结果的        @Override        public void init() {            result= new HashMap<Integer , Integer >() ;        }        //每次对一个新值进行聚集计算都会调用iterate方法        public boolean iterate(Integer key , Integer value)        {            if (result == null )                result=new  HashMap<Integer , Integer >();                            if(result.containsKey(key))            {                int new_value = result.get(key) ;                if (new_value < value)                    new_value = value ;                result.put(key , new_value) ;            }            else                 result.put( key , value ) ;                        return true;        }                                                                                                                                         //Hive需要部分聚集结果的时候会调用该方法        //会返回一个封装了聚集计算当前状态的对象        public HashMap<Integer , Integer > terminatePartial()        {            return result;        }        //合并两个部分聚集值会调用这个方法        public boolean merge(HashMap<Integer , Integer > other)        {            //将 result和other合并            Iterator<Entry<Integer, Integer>> iter = other.entrySet().iterator();            while (iter.hasNext()) {                    Map.Entry<Integer , Integer> entry =  iter.next();                    int key = entry.getKey();                    int val = entry.getValue();                            if(result.containsKey(key))                {                    int new_value = result.get(key) ;                    if (new_value < val)                        new_value = val ;                    result.put(key , new_value) ;                }                else                     result.put( key , val ) ;            }            return true ;        }        //Hive需要最终聚集结果时候会调用该方法        public ArrayList<Integer> terminate()        {            ArrayList<Integer> res = new ArrayList<Integer>() ;            //先找出最大的value            int max_value = Integer.MIN_VALUE ;            Iterator<Entry<Integer, Integer>> iter = result.entrySet().iterator();            while (iter.hasNext()) {                    Map.Entry<Integer , Integer> entry =  iter.next();                    int val = entry.getValue();                                        if (val > max_value)                        max_value = val ;            }                        Iterator<Entry<Integer, Integer>> iter2 = result.entrySet().iterator();            while (iter2.hasNext()) {                    Map.Entry<Integer , Integer> entry =  iter2.next();                    int key = entry.getKey();                    int val = entry.getValue();                                        if (val == max_value)                        res.add(key) ;            }            return res ;        }    }}