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

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

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

july里面的分析如下。

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

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

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

我的分析:

1、  见july的1st.

2、  见july的2nd.

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的分析,取每个IP的hashCode,与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.0分3.6秒

4、第四次分割1亿数据的大文件,经过优化后,耗时13.0分35.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次数最多的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秒

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));
}

海量日志数据提取某日访问百度次数最多的那个IP的Java实现的更多相关文章

  1. 14海量日志提取出现次数最多的IP

    问题描述:现有某网站海量日志数据,提取出某日访问该网站次数最多的那个IP. 分析:IP地址是32位的二进制数,所以共有N=2^32=4G个不同的IP地址, 如果将每个IP地址看做是数组的索引的话,那么 ...

  2. 一次flume exec source采集日志到kafka因为单条日志数据非常大同步失败的踩坑带来的思考

    本次遇到的问题描述,日志采集同步时,当单条日志(日志文件中一行日志)超过2M大小,数据无法采集同步到kafka,分析后,共踩到如下几个坑.1.flume采集时,通过shell+EXEC(tail -F ...

  3. 海量日志收集利器&#160;——&#160;Flume

    Flume 是什么? Flume是一个分布式.可靠.和高可用的海量日志聚合的系统,支持在系统中定制各类数据发送方,用于收集数据:同时,Flume提供对数据进行简单处理,并写到各种数据接受方(可定制)的 ...

  4. 面试笔试-脚本-1:使用shell脚本输出登录次数最多的用户

    原题目: 一个文本类型的文件,里面每行存放一个登陆者的IP(某些行是反复的),写一个shell脚本输出登陆次数最多的用户. 之前刚看到这个题目时,立即没有想到一行直接解决的办法,尽管知道能够先进行排序 ...

  5. 使用python找出nginx访问日志中访问次数最多的10个ip排序生成网页

    使用python找出nginx访问日志中访问次数最多的10个ip排序生成网页 方法1:linux下使用awk命令 # cat access1.log | awk '{print $1" &q ...

  6. Python习题-统计日志中访问次数超过限制的IP

    #1.1分钟之内ip访问次数超过200次的,就给他的ip加入黑名单#需求分析: #1.读日志,1分钟读一次 #2.获取这1分钟之内所有访问的ip #3.判断ip出现的次数,如果出现200次,那么就加入 ...

  7. 大数据学习——有两个海量日志文件存储在hdfs

    有两个海量日志文件存储在hdfs上, 其中登陆日志格式:user,ip,time,oper(枚举值:1为上线,2为下线):访问之日格式为:ip,time,url,假设登陆日志中上下线信息完整,切同一上 ...

  8. MongoDB应用案例:使用 MongoDB 存储日志数据

    线上运行的服务会产生大量的运行及访问日志,日志里会包含一些错误.警告.及用户行为等信息,通常服务会以文本的形式记录日志信息,这样可读性强,方便于日常定位问题,但当产生大量的日志之后,要想从大量日志里挖 ...

  9. 使用 MongoDB 存储日志数据

    使用 MongoDB 存储日志数据     线上运行的服务会产生大量的运行及访问日志,日志里会包含一些错误.警告.及用户行为等信息.通常服务会以文本的形式记录日志信息,这样可读性强,方便于日常定位问题 ...

随机推荐

  1. [转]win7下apache2.4响应很慢解决方法

    win7下apache2.4响应很慢解决方法 PS.按照以下方法测试了以下,似乎确实快了一点[skysowe] 转载自: http://blog.sina.com.cn/s/blog_75ad1010 ...

  2. A Plug for UNIX 分类: POJ 图论 函数 2015-08-10 14:18 2人阅读 评论(0) 收藏

    A Plug for UNIX Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 14786 Accepted: 4994 Desc ...

  3. 【转】实现RTP协议的H.264视频传输系统

    1.  引言       随着信息产业的发展,人们对信息资源的要求已经逐渐由文字和图片过渡到音频和视频,并越来越强调获取资源的实时性和互动性.但人们又面临着另外一种不可避免的尴尬,就是在网络上看到生动 ...

  4. UML的9种图例解析

    摘自: http://blog.csdn.net/fatherican/article/details/44966891 UML图中类之间的关系:依赖,泛化,关联,聚合,组合,实现 类与类图 1) 类 ...

  5. Regex Tester 安装教程

    下载com.brosinski.eclipse.regex_1.4.0.jar 地址:https://github.com/sbrosinski/RegexTester 下载之后把jar包粘贴到${e ...

  6. 利用JS_SDK实现QQ第三方登录

    前言 现如今,第三方登录已成为大部分网站必备的一项基础技能,引入时髦的第三方登录不仅能帮你吸引更多的用户,也让你的网站可以充分利用其他大型网站的用户资源.本次教程将让你的网站最快捷便利地引入QQ登录. ...

  7. while循环的跳出

    今天在编码时突然产生一个疑问:程序中有一个while循环,循环体执行的是某个附带条件限制的操作.我现在想达到的目的是 => 条件成立,就执行操作,并跳出循环:条件不成立就跳出当次的while循环 ...

  8. iOS 视图调试器(Debug View Hierarchy) 之 初试牛刀

    参考:http://blog.csdn.net/th_gsb/article/details/44856795 由于iOS的界面开发大多都是用代码实现的,编写的时候,那就是看不见摸不着的情况.所以,如 ...

  9. 【javascript】详解javascript闭包 — 大家准备好瓜子,我要开始讲故事啦~~

    前言: 在这篇文章里,我将对那些在各种有关闭包的资料中频繁出现,但却又千篇一律,且暧昧模糊得让人难以理解的表述,做一次自己的解读.或者说是对“红宝书”的<函数表达式/闭包>的那一章节所写的 ...

  10. 报错:/usr/sbin/mysqld: Can&#39;t find file: &#39;./performance_schema/events_waits_summary_by_account_by_event_name.frm&#39; (errno: 13 - Permission denied)

    报错背景: Linux环境下安装MySQL数据库. 安装完成,数据库初始化,启动数据库时报错. 报错现象: -- :: [ERROR] Native table 'performance_schema ...