MapReduce实现手机上网日志分析(分区)
一、问题背景
实际业务的需要,比如以移动为例,河南的用户去了北京上网,那么他的上网信息默认保存在了北京的基站,那么我们想要查询北京地区的上网日志信息默认也包含了其他地区用户的在本区的上网信息,否则只能扫描日志找到北京,很慢,所以分区很需要。
二、数据集分析
1363157985066 13726230503 00-FD-07-A4-72-B8:CMCC 120.196.100.82 i02.c.aliimg.com 24 27 2481 24681 200 1363157995052 13826544101 5C-0E-8B-C7-F1-E0:CMCC 120.197.40.4 4 0 264 0 200 1363157991076 13926435656 20-10-7A-28-CC-0A:CMCC 120.196.100.99 2 4 132 1512 200 1363154400022 13926251106 5C-0E-8B-8B-B1-50:CMCC 120.197.40.4 4 0 240 0 200 1363157993044 18211575961 94-71-AC-CD-E6-18:CMCC-EASY 120.196.100.99 iface.qiyi.com 视频网站 15 12 1527 2106 200 1363157995074 84138413 5C-0E-8B-8C-E8-20:7DaysInn 120.197.40.4 122.72.52.12 20 16 4116 1432 200 1363157993055 13560439658 C4-17-FE-BA-DE-D9:CMCC 120.196.100.99 18 15 1116 954 200 1363157995033 15920133257 5C-0E-8B-C7-BA-20:CMCC 120.197.40.4 sug.so.360.cn 信息安全 20 20 3156 2936 200 1363157983019 13719199419 68-A1-B7-03-07-B1:CMCC-EASY 120.196.100.82 4 0 240 0 200 1363157984041 13660577991 5C-0E-8B-92-5C-20:CMCC-EASY 120.197.40.4 s19.cnzz.com 站点统计 24 9 6960 690 200 1363157973098 15013685858 5C-0E-8B-C7-F7-90:CMCC 120.197.40.4 rank.ie.sogou.com 搜索引擎 28 27 3659 3538 200 1363157986029 15989002119 E8-99-C4-4E-93-E0:CMCC-EASY 120.196.100.99 www.umeng.com 站点统计 3 3 1938 180 200 1363157992093 13560439658 C4-17-FE-BA-DE-D9:CMCC 120.196.100.99 15 9 918 4938 200 1363157986041 13480253104 5C-0E-8B-C7-FC-80:CMCC-EASY 120.197.40.4 3 3 180 180 200 1363157984040 13602846565 5C-0E-8B-8B-B6-00:CMCC 120.197.40.4 2052.flash2-http.qq.com 综合门户 15 12 1938 2910 200 1363157995093 13922314466 00-FD-07-A2-EC-BA:CMCC 120.196.100.82 img.qfc.cn 12 12 3008 3720 200 1363157982040 13502468823 5C-0A-5B-6A-0B-D4:CMCC-EASY 120.196.100.99 y0.ifengimg.com 综合门户 57 102 7335 110349 200 1363157986072 18320173382 84-25-DB-4F-10-1A:CMCC-EASY 120.196.100.99 input.shouji.sogou.com 搜索引擎 21 18 9531 2412 200 1363157990043 13925057413 00-1F-64-E1-E6-9A:CMCC 120.196.100.55 t3.baidu.com 搜索引擎 69 63 11058 48243 200 1363157988072 13760778710 00-FD-07-A4-7B-08:CMCC 120.196.100.82 2 2 120 120 200 1363157985066 13726238888 00-FD-07-A4-72-B8:CMCC 120.196.100.82 i02.c.aliimg.com 24 27 2481 24681 200 1363157993055 13560436666 C4-17-FE-BA-DE-D9:CMCC 120.196.100.99 18 15 1116 954 200
查看电话号码一列,看前三位分为移动、联通和电信,不过还有以84开头的同意归属为海外,那么我们需要的共有4个reducer,那么需要Partitioner里面需要自己分为四类。
一个reducer对应一个结果文件。
不能再本地运行,这样的话只能一个map,一个reducer,无论设置。
三、理论准备
3.1 抽象类与接口
我们都知道在面向对象的领域一切都是对象,同时所有的对象都是通过类来描述的,但是并不是所有的类都是来描述对象的。如果一个类没有足够的信息来描述一个具体的对象,而需要其他具体的类来支撑它,那么这样的类我们称它为抽象类。比如new Animal(),我们都知道这个是产生一个动物Animal对象,但是这个Animal具体长成什么样子我们并不知道,它没有一个具体动物的概念,所以他就是一个抽象类,需要一个具体的动物,如狗、猫来对它进行特定的描述,我们才知道它长成啥样。
抽象类和普通类的区别是强制让子类去重写弗雷的方法。
public abstract class Animal { public abstract void cry(); } public class Cat extends Animal{ @Override public void cry() { System.out.println("猫叫:喵喵..."); } } public class Dog extends Animal{ @Override public void cry() { System.out.println("狗叫:汪汪..."); } } public class Test { public static void main(String[] args) { Animal a1 = new Cat(); Animal a2 = new Dog(); a1.cry(); a2.cry(); } } -------------------------------------------------------------------- Output: 猫叫:喵喵... 狗叫:汪汪...
抽象层次不同。抽象类是对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。
抽象类所跨域的是具有相似特点的类,而接口却可以跨域不同的类。我们知道抽象类是从子类中发现公共部分,然后泛化成抽象类,子类继承该父类即可,但是接口不同。实现它的子类可以不存在任何关系,共同之处。例如猫、狗可以抽象成一个动物类抽象类,具备叫的方法。鸟、飞机可以实现飞Fly接口,具备飞的行为,这里我们总不能将鸟、飞机共用一个父类吧!所以说抽象类所体现的是一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is-a" 关系,即父类和派生类在概念本质上应该是相同的。对于接口则不然,并不要求接口的实现者和接口定义在概念本质上是一致的, 仅仅是实现了接口定义的契约而已。
3.2 static块与单例
static块会在实例初始化之前执行,所以你可以在方法调用之前进行一些初始化操作,
单例是获取对象的一种方式而已,保证只有一个实现类,
实际开发中几乎用不到,单例spring提供有实现,static在测试的时候可能会用到,还有加载一些系统配置文件的时候可能会把加载写在static块中。
四、代码实现
//Partitioner是map执行完成后reduce还没执行,所以他的类型是map的输出类型 public class DataCountPartitioner extends Partitioner<Text,DataBean> { //没执行一次变读取一次数据库很不好,可以做缓存,或者搞成单利, //为了简单直接搞一个static块 private static Map<String , Integer> dataCountMap = new HashMap<String , Integer>(); static { //静态的从上往下执行,也就是先执行上面的datacoutnMap,否则静态块里 //网datacountmap里棉放东西 dataCountMap.put("135",1); dataCountMap.put("136",1); dataCountMap.put("137",1); dataCountMap.put("138",1); dataCountMap.put("139",1); dataCountMap.put("150",2); dataCountMap.put("159",2); dataCountMap.put("182",2); dataCountMap.put("183",2); } //int表示分区号 //numPartitions:几个reducer就有几个这个值 @Override public int getPartition(Text key, DataBean value, int numPartitions) { // TODO Auto-generated method stub String telNo = key.toString(); //从0开始取3位 String subTelNo = telNo.substring(0, 3); Integer code = dataCountMap.get(subTelNo); //186 843等开头的默认是国外 if(null==code) { code = 0; } return code; } }
五、结果分析
5.1 _SUCCESS
这个没啥用,mapreduce自带的,不过如果你的程序有多部mapreduce,肯定会有中间结果,那么倒是可以这个_success来判断是否执行了上个步骤,也就是说在补数据的时候,如果发现某一步_success了 那么就说明上一步不用补跑mapreduce,直接执行下面的程序.
5.2 结果
查看结果发现0里面是134和841开头的,达到预期,1和2分别是联通,3是空的,为什么呢?因为partitioner里面的分类设置类3个,而reducer个数是4个,其中一个reducer没有数据粉发过去所以就是空的。
那如果reducer个数小于partitioner个数呢,我发现输出文件加油,也没报错,就是空文件夹。需要log4j才能看到报错信息。