zoukankan      html  css  js  c++  java
  • 海量日志数据提取某日访问百度次数最多的那个IP的Java实现

    海量日志数据提取某日访问百度次数最多的那个IPJava实现

     

    前几天在网上看到july的一篇文章《教你如何迅速秒杀掉:99%的海量数据处理面试题,里面说到百度的一个面试题目,题目如下:

    海量日志数据,提取出某日访问百度次数最多的那个IP

     

     

    july里面的分析如下。

    1、  分而治之/hash映射:针对数据太大,内存受限,只能是:把大文件化成(取模映射)小文件,即16字方针:大而化小,各个击破,缩小规模,逐个解决

    2、  hash统计:当大文件转化了小文件,那么我们便可以采用常规的hash_map(ipvalue)来进行频率统计。

    3、  /快速排序:统计完了之后,便进行排序(可采取堆排序),得到次数最多的IP

     

    我的分析:

    1、  july1st.

    2、  july2nd.

    3、  不用排序,直接在统计的时候,计算出次数最多的IP:在第2步的时候,求出ip的次数,实际上呢,次数最大的那个只可能是一个值,因此在计算每个IP次数的时候,与这个最大值作比较,计算完即可知道最大值的IP….

     

     

    1      机器配置:

    CPU:I3-2330M  2.20GHZ

    MEM:4G(3.16G可用)

    OS:win7  32

    2      生成海量数据的大文件:

    2.1     总数据为1亿个IP数据,生成规则:以10.开头,其他是0-255的随机数。

     

    /**
    	 * 生成大文件
    	 * @param ipFile
    	 * @param numberOfLine
    	 */
    	public void gernBigFile(File ipFile,long numberOfLine){
    		BufferedWriter bw = null;
    		FileWriter fw = null;
    		long startTime = System.currentTimeMillis();
    		try{
    			fw = new FileWriter(ipFile,true);
    			bw = new BufferedWriter(fw);
    			
    			SecureRandom random = new SecureRandom();
    			for (int i = 0; i < numberOfLine; i++) {
    				bw.write("10."+random.nextInt(255)+"."+random.nextInt(255)+"."+random.nextInt(255)+"
    ");
    				if((i+1) % 1000 == 0){
    					bw.flush();
    				}
    			}
    			bw.flush();
    			
    			long endTime = System.currentTimeMillis();
    			System.err.println(DateUtil.convertMillsToTime(endTime - startTime));
    		}catch (Exception e) {
    			e.printStackTrace();
    		}finally{
    			try{
    				if(fw != null){
    					fw.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    			try{
    				if(bw != null){
    					bw.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    


    /*
    		 * 1、第一次生成1亿(实际上最多为16581375)的ip地址,需要时间为3分多钟不到4分钟。
    		 */
    		TooMuchIpFile tooMuchIpFile = new TooMuchIpFile();
    		File ipFile = new File("e:/ipAddr.txt");
    		try {
    			ipFile.createNewFile();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		tooMuchIpFile.gernBigFile(ipFile, 100000000);
    


      

     

    2.2     运行结果:

    生成1亿行的Ip地址,大约耗时:3分多钟,大小1.27 GB (1,370,587,382字节)

     

     

    3      分割大文件,

    根据july的分析,取每个IPhashCode,与1000取模,把IP散列到不同的文件中去。

     

    3.1     第一种方法:

    一边取每个IP的散列值,再模1000,得到一个值,然后写到此值对应的文件中去。大约耗时超过2个多小时,实在是太慢了,没跑完就直接断掉了。

     

    /**
    	 * 大文件分割为小文件
    	 * @param ipFile
    	 * @param numberOfFile
    	 */
    	public void splitFile(File ipFile,int numberOfFile){
    		BufferedReader br = null;
    		FileReader fr = null;
    		BufferedWriter bw = null;
    		FileWriter fw = null;
    		long startTime = System.currentTimeMillis();
    		try{
    			fr = new FileReader(ipFile);
    			br = new BufferedReader(fr);
    			String ipLine = br.readLine();
    			while(ipLine != null){
    				int hashCode = ipLine.hashCode();
    				hashCode = hashCode < 0 ? -hashCode : hashCode;
    				int fileNum = hashCode % numberOfFile;
    				File file = new File("e:/tmp/ip/"+ fileNum + ".txt");
    				if(!file.exists()){
    					file.createNewFile();
    				}
    				fw = new FileWriter(file,true);
    				bw = new BufferedWriter(fw);
    				bw.write(ipLine + "
    ");
    				bw.flush();
    				fw.close();
    				bw.close();
    				ipLine = br.readLine();
    			}
    			
    			long endTime = System.currentTimeMillis();
    			System.err.println(DateUtil.convertMillsToTime(endTime - startTime));
    		}catch (Exception e) {
    			e.printStackTrace();
    		}finally{
    			try{
    				if(fr != null){
    					fr.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    			try{
    				if(br != null){
    					br.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    			try{
    				if(fw != null){
    					fw.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    			try{
    				if(bw != null){
    					bw.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    


     

     

    3.2     第二种方法:

    与第一次方法基本相同,不同的是减少流对象的创建,只是创建文件时,创建流对象,但还是需要每次都要判断文件存在与否。大约耗时超过1个多小时,也实在是慢呀,没等它运行完就断了。

    /**
    	 * 大文件分割为小文件
    	 * @param ipFile
    	 * @param numberOfFile
    	 */
    	public void splitFile2(File ipFile,int numberOfFile){
    		BufferedReader br = null;
    		FileReader fr = null;
    		BufferedWriter bw = null;
    		FileWriter fw = null;
    		long startTime = System.currentTimeMillis();
    		try{
    			fr = new FileReader(ipFile);
    			br = new BufferedReader(fr);
    			String ipLine = br.readLine();
    			while(ipLine != null){
    				int hashCode = ipLine.hashCode();
    				hashCode = hashCode < 0 ? -hashCode : hashCode;
    				int fileNum = hashCode % numberOfFile;
    				File file = new File("e:/tmp/ip/"+ fileNum + ".txt");
    				if(!file.exists()){
    					file.createNewFile();
    					fw = new FileWriter(file,true);
    					bw = new BufferedWriter(fw);
    					bwMap.put(fileNum, bw);
    				}else{
    					bw = bwMap.get(fileNum);
    				}
    				bw.write(ipLine + "
    ");
    				bw.flush();
    				ipLine = br.readLine();
    			}
    			for(int fn : bwMap.keySet()){
    				bwMap.get(fn).close();
    			}
    			bwMap.clear();
    			long endTime = System.currentTimeMillis();
    			System.err.println(DateUtil.convertMillsToTime(endTime - startTime));
    		}catch (Exception e) {
    			e.printStackTrace();
    		}finally{
    			try{
    				if(fr != null){
    					fr.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    			try{
    				if(br != null){
    					br.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    			try{
    				if(fw != null){
    					fw.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    			try{
    				if(bw != null){
    					bw.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    


     

    3.3     第三种方法:

    与第二种方法基本相同,在此基础上,优化一边取值,一边写文件的过程,而是先写到内存中,当达到1000后,再一起写入文件中。大约耗时52多分钟,这个是实际运行完的,在中午去吃饭的时候让它自己跑完的。

    /**
    	 * 大文件分割为小文件
    	 * @param ipFile
    	 * @param numberOfFile
    	 */
    	public void splitFile3(File ipFile,int numberOfFile){
    		BufferedReader br = null;
    		FileReader fr = null;
    		BufferedWriter bw = null;
    		FileWriter fw = null;
    		long startTime = System.currentTimeMillis();
    		try{
    			fr = new FileReader(ipFile);
    			br = new BufferedReader(fr);
    			String ipLine = br.readLine();
    			while(ipLine != null){
    				int hashCode = ipLine.hashCode();
    				hashCode = hashCode < 0 ? -hashCode : hashCode;
    				int fileNum = hashCode % numberOfFile;
    				File file = new File("e:/tmp/ip/"+ fileNum + ".txt");
    				if(!file.exists()){
    					file.createNewFile();
    					fw = new FileWriter(file,true);
    					bw = new BufferedWriter(fw);
    					bwMap.put(fileNum, bw);
    					dataMap.put(fileNum, new LinkedList<String>());
    				}else{
    					List<String> list = dataMap.get(fileNum);
    					list.add(ipLine + "
    ");
    					if(list.size() % 1000 == 0){
    						BufferedWriter writer = bwMap.get(fileNum);
    						for(String line : list){
    							writer.write(line);
    						}
    						writer.flush();
    						list.clear();
    					}
    				}
    				ipLine = br.readLine();
    			}
    			for(int fn : bwMap.keySet()){
    				List<String> list = dataMap.get(fn);
    				BufferedWriter writer = bwMap.get(fn);
    				for(String line : list){
    					writer.write(line);
    				}
    				list.clear();
    				writer.flush();
    				writer.close();
    			}
    			bwMap.clear();
    			long endTime = System.currentTimeMillis();
    			System.err.println(DateUtil.convertMillsToTime(endTime - startTime));
    		}catch (Exception e) {
    			e.printStackTrace();
    		}finally{
    			try{
    				if(fr != null){
    					fr.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    			try{
    				if(br != null){
    					br.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    			try{
    				if(fw != null){
    					fw.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    			try{
    				if(bw != null){
    					bw.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    


     

    3.4     第四种方法:

    在第三种方法基础上作进一步优化,不同的是,把创建1000个流对象放到循环外面。大约耗时13分钟35秒。这个方法实在比第三种方法快了4倍左右,但在我觉得,这时间还是有点说不过去呀。

    /**
    	 * 大文件分割为小文件
    	 * @param ipFile
    	 * @param numberOfFile
    	 */
    	public void splitFile4(File ipFile,int numberOfFile){
    		BufferedReader br = null;
    		FileReader fr = null;
    		BufferedWriter bw = null;
    		FileWriter fw = null;
    		long startTime = System.currentTimeMillis();
    		try{
    			fr = new FileReader(ipFile);
    			br = new BufferedReader(fr);
    			String ipLine = br.readLine();
    			//先创建文件及流对象方便使用
    			for(int i=0;i<numberOfFile;i++){
    				File file = new File("e:/tmp/ip1/"+ i + ".txt");
    				bwMap.put(i, new BufferedWriter(new FileWriter(file,true)));
    				dataMap.put(i, new LinkedList<String>());
    			}
    			while(ipLine != null){
    				int hashCode = ipLine.hashCode();
    				hashCode = hashCode < 0 ? -hashCode : hashCode;
    				int fileNum = hashCode % numberOfFile;
    				List<String> list = dataMap.get(fileNum);
    				list.add(ipLine + "
    ");
    				if(list.size() % 1000 == 0){
    					BufferedWriter writer = bwMap.get(fileNum);
    					for(String line : list){
    						writer.write(line);
    					}
    					writer.flush();
    					list.clear();
    				}
    				ipLine = br.readLine();
    			}
    			for(int fn : bwMap.keySet()){
    				List<String> list = dataMap.get(fn);
    				BufferedWriter writer = bwMap.get(fn);
    				for(String line : list){
    					writer.write(line);
    				}
    				list.clear();
    				writer.flush();
    				writer.close();
    			}
    			bwMap.clear();
    			long endTime = System.currentTimeMillis();
    			System.err.println(DateUtil.convertMillsToTime(endTime - startTime));
    		}catch (Exception e) {
    			e.printStackTrace();
    		}finally{
    			try{
    				if(fr != null){
    					fr.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    			try{
    				if(br != null){
    					br.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    			try{
    				if(fw != null){
    					fw.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    			try{
    				if(bw != null){
    					bw.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    


     

     

    3.5     第五种方法:

    使用多线程,未成功实现优化。只是给出思路如下:读取1亿数据的文件,循环读取每个IP,计算其散列值,取模1000,之后把其放到对应的队列中,当其队列超过1000时,启动一个服务线程把数据写入文件中。(也即主线程只负责计算,由其他线程负责写)

     

     

    3.6     运行结果:

    1、第一次分割1亿数据的大文件,实在是太慢,运行差不多一小时,才分割出300W数据,耗时超过2个钟头

    2、第二次分割1亿数据的大文件,经过优化后,虽然比第一次有提升,但是还是很慢,耗时超过1个钟头.

    3、第三次分割1亿数据的大文件,经过优化后,虽然比第二次有提升,但是还是很慢,需耗时52.03.6

    4、第四次分割1亿数据的大文件,经过优化后,耗时13.035.10400000000004

    4      统计

    各个文件中出现次数最多的IP(可能有多个):

    采用的方法是一边统计各个IP出现的次数,一边算次数出现最大那个IP

     

    /**
    	 * 统计,找出次数最多的IP
    	 * @param ipFile
    	 */
    	public void read(File ipFile){
    		BufferedReader br = null;
    		FileReader fr = null;
    		long startTime = System.currentTimeMillis();
    		try{
    			fr = new FileReader(ipFile);
    			br = new BufferedReader(fr);
    			String ipLine = br.readLine();
    			while(ipLine != null){
    				ipLine = ipLine.trim();
    				Integer count = ipNumMap.get(ipLine);
    				if(count == null){
    					count = 0;
    				}
    				count ++;
    				ipNumMap.put(ipLine, count);
    				
    				if(count >= ipMaxNum){
    					if(count > ipMaxNum){
    						keyList.clear();
    					}
    					keyList.add(ipLine);
    					ipMaxNum = count;
    				}
    				ipLine = br.readLine();
    			}
    			long endTime = System.currentTimeMillis();
    			System.err.println(ipFile.getName()+":"+DateUtil.convertMillsToTime(endTime - startTime));
    			totalTime += (endTime - startTime);
    		}catch (Exception e) {
    			e.printStackTrace();
    		}finally{
    			try{
    				if(fr != null){
    					fr.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    			try{
    				if(br != null){
    					br.close();
    				}
    			}catch (Exception e) {
    				e.printStackTrace();
    			}
    		}
    	}
    


     

    4.1     运行结果:

    1、从1000个文件中查询Ip次数最多的Ip10.164.143.57:24,3.018.748999999999995

    2、从1000个文件中查询Ip次数最多的Ip10.164.143.57:24,3.027.366000000000014

    3、从1000个文件中查询Ip次数最多的Ip10.164.143.57:24,2.042.781000000000006

     

     

    5      以上代码的公共变量

    public final Map<Integer,BufferedWriter> bwMap = new HashMap<Integer,BufferedWriter>();//保存每个文件的流对象
    public final Map<Integer,List<String>> dataMap = new HashMap<Integer,List<String>>();//分隔文件用
    private Map<String,Integer> ipNumMap = new HashMap<String, Integer>();//保存每个文件中的每个IP出现的次数
    	private List<String> keyList = new LinkedList<String>();//保存次数出现最多的IP
    	private int ipMaxNum = 0;//次数出现最多的值
    	private long totalTime = 0;//计算统计所耗的时间
    
    


     

     

    6      Main

    public static void main(String[] args) {
    /*
    	 * 1、第一次生成1亿(实际上最多为16581375)的ip地址,需要时间为3分多钟不到4分钟。
    	 */
    		/*TooMuchIpFile tooMuchIpFile = new TooMuchIpFile();
    		File ipFile = new File("e:/ipAddr.txt");
    		try {
    			ipFile.createNewFile();
    		} catch (IOException e) {
    			e.printStackTrace();
    		}
    		tooMuchIpFile.gernBigFile(ipFile, 100000000);*/
    
    	
    //		System.err.println("128.128.80.226".hashCode()%1000);
    //		System.err.println("128.128.80.227".hashCode());
    //		System.err.println("10.128.80.227".hashCode());
    //		System.err.println("10.0.80.227".hashCode());
    		
    		
    		/*
    		 * 1、第一次分割1亿数据的大文件,实在是太慢,运行差不多一小时,才分割出300W数据,耗时超过2个钟头
    		 * 2、第二次分割1亿数据的大文件,经过优化后,虽然比第一次有提升,但是还是很慢,耗时超过1个钟头.
    		 * 3、第三次分割1亿数据的大文件,经过优化后,虽然比第二次有提升,但是还是很慢,需耗时52.0分3.6秒
    		 * 4、第四次分割1亿数据的大文件,经过优化后,耗时13.0分35.10400000000004秒
    	 */
    		TooMuchIpFile tooMuchIpFile = new TooMuchIpFile();
    		File ipFile = new File("e:/ipAddr.txt");
    		tooMuchIpFile.splitFile4(ipFile, 1000);
    		
    		
    		/*
    		 * 1、从1000个文件中查询Ip次数最多的Ip,10.164.143.57:24,3.0分18.748999999999995秒
    		 * 2、从1000个文件中查询Ip次数最多的Ip,10.164.143.57:24,3.0分27.366000000000014秒
    		 * 3、从1000个文件中查询Ip次数最多的Ip,10.164.143.57:24,2.0分42.781000000000006秒
    		 */
    //		TooMuchIpFile tooMuchIpFile = new TooMuchIpFile();
    //		File ipFiles = new File("e:/tmp/ip1/");
    //		for (File ipFile : ipFiles.listFiles()) {
    //			tooMuchIpFile.read(ipFile);
    //			tooMuchIpFile.ipNumMap.clear();
    //		}
    //		System.err.println("======================出现次数最多的IP==================");
    //		for(String key: tooMuchIpFile.keyList){
    //			System.err.println(key + ":" + tooMuchIpFile.ipMaxNum);
    //		}
    //		System.err.println(DateUtil.convertMillsToTime(tooMuchIpFile.totalTime));
    	}
    


     

  • 相关阅读:
    Eclipse CDT Linux下内存分析 实战历险
    .Net元编程【Metaprogramming in NET】 序-翻译
    go语言和资料
    代码提交 【转】
    两本有意思的书【代码的未来、淘宝技术这十年】
    C/C++构建系统 GNU autotool
    C/C++构建系统 -工具汇总
    使用Java语言开发微信公众平台(四)——图文消息的发送与响应
    Onsen UI 前端框架(二)
    Maven项目搭建(一):Maven初体验
  • 原文地址:https://www.cnblogs.com/james1207/p/3341668.html
Copyright © 2011-2022 走看看