锁定老帖子 主题:这不是面试题,这是真事
精华帖 (0) :: 良好帖 (1) :: 新手帖 (1) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2013-08-01
如果只有两亿条数据,每一条数据占用8字节(long),内存也才1.5G左右(Set),就算加上其它的,4G的内存已经足够了。。。写入的时候可以批量写(比如通过StringBuilder缓存,大于4m就写入文件),读取的时候就用BufferedReader,50G的文件,处理下来应该在25分钟以下,20分钟左右,主要就是读取文件比较耗时间。
|
|
返回顶楼 | |
发表时间: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开始的,这样就减少了寻道时间,也避免了创建那么大的临时文件。 这些就是我个人的见解,请各位兄台拍砖。 |
|
返回顶楼 | |
发表时间:2013-08-01
楼主这问题解决没?能解决的话可以发个例子到我邮箱么? tomakemyself@163.com
|
|
返回顶楼 | |
发表时间: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开始的,这样就减少了寻道时间,也避免了创建那么大的临时文件。 这些就是我个人的见解,请各位兄台拍砖。 可行,但每读一行数据写一次文件,不知道性能怎么样,没测试过 |
|
返回顶楼 | |
发表时间: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开始的,这样就减少了寻道时间,也避免了创建那么大的临时文件。 这些就是我个人的见解,请各位兄台拍砖。 可行,但每读一行数据写一次文件,不知道性能怎么样,没测试过 你们三儿,以及前面那些说排序的,用你们浅薄的见识,已经成功把这里变成垃圾桶了 |
|
返回顶楼 | |
发表时间: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分钟左右。 |
|
返回顶楼 | |
发表时间: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文件用了多长时间? |
|
返回顶楼 | |
发表时间: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秒左右。 |
|
返回顶楼 | |
发表时间:2013-08-06
用Go写了程序,比上面的用时多了1倍,总共将近17分钟,其中读50G大文件、拆分写小文件这个过程用了15分钟,处理小文件并输入最终结果2分钟。最终的结果文件690MB大小。
不多说,上代码:https://github.com/hardPass/50G_15_200MBlines 上面叫的欢的,这个“可行”、那个“可行”的,你倒是上代码啊!纸上谈兵! |
|
返回顶楼 | |
发表时间:2013-08-06
hardPass 写道 用Go写了程序,比上面的用时多了1倍,总共将近17分钟,其中读50G大文件、拆分写小文件这个过程用了15分钟,处理小文件并输入最终结果2分钟。最终的结果文件690MB大小。
不多说,上代码:https://github.com/hardPass/50G_15_200MBlines 上面那几个对seek 叫的欢的,这个“可行”、那个“可行”的,你倒是上代码啊!纸上谈兵! |
|
返回顶楼 | |