论坛首页 Java企业应用论坛

这不是面试题,这是真事

浏览 39063 次
精华帖 (0) :: 良好帖 (1) :: 新手帖 (1) :: 隐藏帖 (0)
作者 正文
   发表时间:2013-08-01  
如果只有两亿条数据,每一条数据占用8字节(long),内存也才1.5G左右(Set),就算加上其它的,4G的内存已经足够了。。。写入的时候可以批量写(比如通过StringBuilder缓存,大于4m就写入文件),读取的时候就用BufferedReader,50G的文件,处理下来应该在25分钟以下,20分钟左右,主要就是读取文件比较耗时间。
0 请登录后投票
   发表时间:2013-08-01  
看了关于这个主题那么多的讨论,我也说一下自己的看法。
现在讨论提供的就是两种方式,第一个是把50G的文件进行分拆,我也同意楼主的说法,把那么多的数据分拆到不同文件是个技术活,难度不小,我的想法不是从技术上实现的,我的想法是是否可能得到这15个数字是如何生成的,这个数据能否是有其他的列组合生成的,如果可以知道这个数据的生成规则,就可以利用这个规则分拆文件,就可以使相同的数据落到同一个文件里面,比如这个数据由“规则1+规则2+规则3”组成,我第一次按规则1分拆文件,生成的每个文件以规则1来命名,而数据只保存“规则2+规则3”的部分,这样数据量就大大的减少,这样就按照规则一直分拆下去,最后可能生成很多文件,但是文件保存的数据量就大大的减少,文件名+数据就可以标识这个15位的数据,这样开启多线程处理分析这些分拆的文件效率应该不错的吧。

第二个方式是用位图算法来计算,yanyilin224老兄的步骤是可行的,我的想法是在他的基础上进行一下优化,在他
yanyilin224 写道
既然第二列存的是个整数,也不会出现极端情况(比如同一个数字出现Interger.Max_Value次),那么不用分文件,不依赖第三档库,用java.io就能搞定,对内存要求很低,只需要一个2.2G左右的临时文件。
大致算法:
1.创建临时文件
2.读取要解析的文件,每次读一行数据,读到第二列的整数n
3.利用RandomAccessFile读取临时文件的第n个byte(file.seek(n);file.read(byte[1]);)
4.如果读不到数据,或读到的是0,那么向这个位上写入一个1
5.如果读到的是大于0的数字,那么把该数字+1之后再回文件这个byte上(按楼主的要求,如果大于2的话不再写入也正确,因为用于计数的只有一个byte,记录不了太多重复)
6.读取完成后,遍历一次临时文件,如果读到第x位的值大于2,说明整数x出现过超过两次或以上

第二步之前,先全部扫描一遍文件找到这个列的最小值,然后在他的第二步的基础上,读到的数据减去这个最小值,就可以保证写临时文件是从0开始的,这样就减少了寻道时间,也避免了创建那么大的临时文件。

这些就是我个人的见解,请各位兄台拍砖。
0 请登录后投票
   发表时间:2013-08-01  
楼主这问题解决没?能解决的话可以发个例子到我邮箱么? tomakemyself@163.com
0 请登录后投票
   发表时间:2013-08-01  
liuqian5036 写道
看了关于这个主题那么多的讨论,我也说一下自己的看法。
现在讨论提供的就是两种方式,第一个是把50G的文件进行分拆,我也同意楼主的说法,把那么多的数据分拆到不同文件是个技术活,难度不小,我的想法不是从技术上实现的,我的想法是是否可能得到这15个数字是如何生成的,这个数据能否是有其他的列组合生成的,如果可以知道这个数据的生成规则,就可以利用这个规则分拆文件,就可以使相同的数据落到同一个文件里面,比如这个数据由“规则1+规则2+规则3”组成,我第一次按规则1分拆文件,生成的每个文件以规则1来命名,而数据只保存“规则2+规则3”的部分,这样数据量就大大的减少,这样就按照规则一直分拆下去,最后可能生成很多文件,但是文件保存的数据量就大大的减少,文件名+数据就可以标识这个15位的数据,这样开启多线程处理分析这些分拆的文件效率应该不错的吧。

第二个方式是用位图算法来计算,yanyilin224老兄的步骤是可行的,我的想法是在他的基础上进行一下优化,在他
yanyilin224 写道
既然第二列存的是个整数,也不会出现极端情况(比如同一个数字出现Interger.Max_Value次),那么不用分文件,不依赖第三档库,用java.io就能搞定,对内存要求很低,只需要一个2.2G左右的临时文件。
大致算法:
1.创建临时文件
2.读取要解析的文件,每次读一行数据,读到第二列的整数n
3.利用RandomAccessFile读取临时文件的第n个byte(file.seek(n);file.read(byte[1]);)
4.如果读不到数据,或读到的是0,那么向这个位上写入一个1
5.如果读到的是大于0的数字,那么把该数字+1之后再回文件这个byte上(按楼主的要求,如果大于2的话不再写入也正确,因为用于计数的只有一个byte,记录不了太多重复)
6.读取完成后,遍历一次临时文件,如果读到第x位的值大于2,说明整数x出现过超过两次或以上

第二步之前,先全部扫描一遍文件找到这个列的最小值,然后在他的第二步的基础上,读到的数据减去这个最小值,就可以保证写临时文件是从0开始的,这样就减少了寻道时间,也避免了创建那么大的临时文件。

这些就是我个人的见解,请各位兄台拍砖。

可行,但每读一行数据写一次文件,不知道性能怎么样,没测试过
0 请登录后投票
   发表时间:2013-08-01  
wgy07 写道
liuqian5036 写道
看了关于这个主题那么多的讨论,我也说一下自己的看法。
现在讨论提供的就是两种方式,第一个是把50G的文件进行分拆,我也同意楼主的说法,把那么多的数据分拆到不同文件是个技术活,难度不小,我的想法不是从技术上实现的,我的想法是是否可能得到这15个数字是如何生成的,这个数据能否是有其他的列组合生成的,如果可以知道这个数据的生成规则,就可以利用这个规则分拆文件,就可以使相同的数据落到同一个文件里面,比如这个数据由“规则1+规则2+规则3”组成,我第一次按规则1分拆文件,生成的每个文件以规则1来命名,而数据只保存“规则2+规则3”的部分,这样数据量就大大的减少,这样就按照规则一直分拆下去,最后可能生成很多文件,但是文件保存的数据量就大大的减少,文件名+数据就可以标识这个15位的数据,这样开启多线程处理分析这些分拆的文件效率应该不错的吧。

第二个方式是用位图算法来计算,yanyilin224老兄的步骤是可行的,我的想法是在他的基础上进行一下优化,在他
yanyilin224 写道
既然第二列存的是个整数,也不会出现极端情况(比如同一个数字出现Interger.Max_Value次),那么不用分文件,不依赖第三档库,用java.io就能搞定,对内存要求很低,只需要一个2.2G左右的临时文件。
大致算法:
1.创建临时文件
2.读取要解析的文件,每次读一行数据,读到第二列的整数n
3.利用RandomAccessFile读取临时文件的第n个byte(file.seek(n);file.read(byte[1]);)
4.如果读不到数据,或读到的是0,那么向这个位上写入一个1
5.如果读到的是大于0的数字,那么把该数字+1之后再回文件这个byte上(按楼主的要求,如果大于2的话不再写入也正确,因为用于计数的只有一个byte,记录不了太多重复)
6.读取完成后,遍历一次临时文件,如果读到第x位的值大于2,说明整数x出现过超过两次或以上

第二步之前,先全部扫描一遍文件找到这个列的最小值,然后在他的第二步的基础上,读到的数据减去这个最小值,就可以保证写临时文件是从0开始的,这样就减少了寻道时间,也避免了创建那么大的临时文件。

这些就是我个人的见解,请各位兄台拍砖。

可行,但每读一行数据写一次文件,不知道性能怎么样,没测试过


你们三儿,以及前面那些说排序的,用你们浅薄的见识,已经成功把这里变成垃圾桶了
0 请登录后投票
   发表时间:2013-08-01   最后修改:2013-08-01
我来讲讲我的处理方法,就是分、合两个阶段,我大概用了9分多钟处理完数据。分:因为我的是随机数据,所有采用按照首字母来分,分为0-9共十个文件,这样大概每个文件有234M左右,大概要6分钟时间。。。其次是合,Executors.newSingleThreadExecutor(),10个Runnalbe,每个处理一个文件,这样的话就不会超过内存限制(2G),发生堆栈溢出。。。分的阶段主要代码:
while ((line = reader.readLine()) != null) {
int begin = line.indexOf(',');
int end = line.indexOf(',', begin + 1);
String value = line.substring(begin + 1, end);
int first = value.charAt(0) - '0';
if(countArr[first] < countPerLine){
sbArr[first].append(value.substring(1) + ",");
countArr[first] += 1;
}else{
sbArr[first].append(value.substring(1) + "\n");
countArr[first] = 0;
}
if(sbArr[first].length() > bufferSize){
writerSet[first].write(sbArr[first].toString());
sbArr[first] = new StringBuilder();
}
}
合的阶段主要代码:
while ((line = reader.readLine()) != null) {
String[] content = line.split(",");
for (int i = 0; i < content.length; i++) {
Long result = Long.parseLong(content[i]);
if (resultSet.contains(result)) {
sb.append(result + "\n");
} else {
resultSet.add(result);
}
if (sb.length() > bufferSize) {
writer.write(sb.toString());
}
}
}
2亿条数据分的阶段6分钟左右,合的阶段3分钟左右,共9分钟左右。

  • 大小: 214 KB
0 请登录后投票
   发表时间:2013-08-02  
chinaagan 写道
我来讲讲我的处理方法,就是分、合两个阶段,我大概用了9分多钟处理完数据。分:因为我的是随机数据,所有采用按照首字母来分,分为0-9共十个文件,这样大概每个文件有234M左右,大概要6分钟时间。。。其次是合,Executors.newSingleThreadExecutor(),10个Runnalbe,每个处理一个文件,这样的话就不会超过内存限制(2G),发生堆栈溢出。。。分的阶段主要代码:
while ((line = reader.readLine()) != null) {
int begin = line.indexOf(',');
int end = line.indexOf(',', begin + 1);
String value = line.substring(begin + 1, end);
int first = value.charAt(0) - '0';
if(countArr[first] < countPerLine){
sbArr[first].append(value.substring(1) + ",");
countArr[first] += 1;
}else{
sbArr[first].append(value.substring(1) + "\n");
countArr[first] = 0;
}
if(sbArr[first].length() > bufferSize){
writerSet[first].write(sbArr[first].toString());
sbArr[first] = new StringBuilder();
}
}
合的阶段主要代码:
while ((line = reader.readLine()) != null) {
String[] content = line.split(",");
for (int i = 0; i < content.length; i++) {
Long result = Long.parseLong(content[i]);
if (resultSet.contains(result)) {
sb.append(result + "\n");
} else {
resultSet.add(result);
}
if (sb.length() > bufferSize) {
writer.write(sb.toString());
}
}
}
2亿条数据分的阶段6分钟左右,合的阶段3分钟左右,共9分钟左右。



总共9分钟,还真快。
老兄,你生成50G文件用了多长时间?
0 请登录后投票
   发表时间:2013-08-02  
对了,请问lz童鞋,我这么认为算是有道理么:

总共15位的数字,分成 xxxx yyy 12345678
前4位,xxxx,是国际电话代号,总数共不到200个
yyy, 是国内各运营商的网段识别号,如139,136,137等,总共31位

如果这么说,前 7位的可能性差不多是: 200*31, 6000个左右?

或者减少可能性,那就是 前6位的可能性 200*5 (5-6这两位数字统计下来只有5个可能性),这样就是1000个左右的可能性,所以如果根据这前6位分,基本不用担心文件描述符限制之类的问题,然后剩下的9位数字,就很好解决了。

今天根据这个规则生成了50G的文件,双线程用时700秒,单线程800秒左右。
0 请登录后投票
   发表时间:2013-08-06  
用Go写了程序,比上面的用时多了1倍,总共将近17分钟,其中读50G大文件、拆分写小文件这个过程用了15分钟,处理小文件并输入最终结果2分钟。最终的结果文件690MB大小。
不多说,上代码:https://github.com/hardPass/50G_15_200MBlines

上面叫的欢的,这个“可行”、那个“可行”的,你倒是上代码啊!纸上谈兵!
0 请登录后投票
   发表时间:2013-08-06  
hardPass 写道
用Go写了程序,比上面的用时多了1倍,总共将近17分钟,其中读50G大文件、拆分写小文件这个过程用了15分钟,处理小文件并输入最终结果2分钟。最终的结果文件690MB大小。
不多说,上代码:https://github.com/hardPass/50G_15_200MBlines

上面那几个对seek 叫的欢的,这个“可行”、那个“可行”的,你倒是上代码啊!纸上谈兵!

0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics