当前位置: 首页 > news >正文

海量日志数据提取某日访问百度次数最多的那个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)+"\n");
				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 + "\n");
				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 + "\n");
				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 + "\n");
					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 + "\n");
				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));
	}


 

 

转载于:https://www.cnblogs.com/james1207/p/3341668.html

相关文章:

  • 与一个在深圳的外国软件工程师的对话
  • Gridview自定义分页
  • C语言学习趣事_关于C语言中的输入输出流
  • ios7下不能录音问题解决
  • iCloud的真正目的:置Windows于死地
  • json数据与字符串的相互转化
  • 服务器跟不上,网站打开慢,服务器自动重启,怎么办?
  • 盒子游戏(湖南省第七届大学生计算机程序设计竞赛)
  • cout设置16进制大写输出
  • Exchange2010 SP1配置证书
  • RPC、RMI、HTTP、REST的区别
  • apache日志轮询技术(cronolog and rotatelogs)小结
  • Lambda表达式和匿名方法中不支持yield return
  • 通过HTML调用C# [架构]
  • 创建dynamics CRM client-side (五) - 使用regular expression (正则表达式)来检查phone number...
  • .pyc 想到的一些问题
  • Django 博客开发教程 8 - 博客文章详情页
  • Flex布局到底解决了什么问题
  • input实现文字超出省略号功能
  • iOS动画编程-View动画[ 1 ] 基础View动画
  • JavaScript创建对象的四种方式
  • MYSQL如何对数据进行自动化升级--以如果某数据表存在并且某字段不存在时则执行更新操作为例...
  • Nginx 通过 Lua + Redis 实现动态封禁 IP
  • NLPIR语义挖掘平台推动行业大数据应用服务
  • PHP 7 修改了什么呢 -- 2
  • PHP 小技巧
  • Python学习之路16-使用API
  • Redis 懒删除(lazy free)简史
  • Spark学习笔记之相关记录
  • 从tcpdump抓包看TCP/IP协议
  • 飞驰在Mesos的涡轮引擎上
  • 高性能JavaScript阅读简记(三)
  • 工作中总结前端开发流程--vue项目
  • 目录与文件属性:编写ls
  • 如何实现 font-size 的响应式
  • 微服务核心架构梳理
  • 一个SAP顾问在美国的这些年
  • PostgreSQL 快速给指定表每个字段创建索引 - 1
  • ​LeetCode解法汇总1276. 不浪费原料的汉堡制作方案
  • #DBA杂记1
  • #pragma 指令
  • (1)(1.13) SiK无线电高级配置(六)
  • (NSDate) 时间 (time )比较
  • (分布式缓存)Redis哨兵
  • (论文阅读笔记)Network planning with deep reinforcement learning
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (续)使用Django搭建一个完整的项目(Centos7+Nginx)
  • (原創) 如何將struct塞進vector? (C/C++) (STL)
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • .bat批处理(六):替换字符串中匹配的子串
  • .NET delegate 委托 、 Event 事件,接口回调
  • .Net Web项目创建比较不错的参考文章
  • .NET 动态调用WebService + WSE + UsernameToken
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .net使用excel的cells对象没有value方法——学习.net的Excel工作表问题