【后端专题】海量数据处理面试题(前置练手题目):给10G的大文件加上行号

前言

后端面试经常会被问到文件读写、大数据处理等问题。而这对一个选手关于OS、并发、数据结构等的考察是很全面的。因此特此开一个专题来完成这系列的学习。
大三的时候面试被问过大数据文件读取与处理、统计的问题,当时模糊回答了外排序、分治的说法,但是具体怎么做完全没有想法。
龙哥(我的一个师兄)今天也让我尝试一下自己手动解决这样的问题。
龙哥一共给了我两个题目:

  1. (入门)一个10G的大文件,每一行加一个行号,并输出到另一个文件上,实现出来。
  2. (进阶)一个10G的大文件,我要统计出现频率TOP 10的词语,实现出来。(同类型的题还有 top 10的ip、 top 10的query)

因此,在这篇博客中将完成第一个入门实验。
实验环境:

  • Win10
  • Java8

数据准备

为了给第二个实验做数据准备,我们这里生成ip地址作为实验数据。

  • 文件是字符串形式的,一个utf-8下一个字符就是一个字节,
  • 对于一个ip 192.168.101.101\n,一共16个字符,实际上肯定会比16小,如192.168.1.1
  • 要生成10GB的文件,则要有:1010241024*1024/16=671088640行,即6.7亿行。
  • 因此,我这里直接生成七亿条
    /**
     * 生成模拟IP数据
     *
     * @param logNum 生成数据条数
     * @param PATH 生成数据的路径
     * @throws IOException I/O error
     */
    public static void generateData(long logNum, String PATH) throws IOException {

        /* 创建文件 */
        File fp = new File(PATH);
        File parentDir = fp.getParentFile();
        if (!parentDir.exists()) {
            parentDir.mkdirs();
        }
        fp.createNewFile();

        /* 生成随机ip写入文件 */
        SecureRandom random = new SecureRandom();
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(fp))) {
            for (int i = 0; i < logNum; i++) {
                if (i % 1000000 == 0)
                    System.out.printf("已经写了:%d百万个ip了,等等吧\n", i / 1000000);
                StringBuilder sb = new StringBuilder();
                sb.append("192.").append(random.nextInt(255)).append(".").append(random.nextInt(255)).append(".")
                        .append(random.nextInt(255)).append('\n');
                bw.write(sb.toString());
            }
            bw.flush();
        }
    }

在Main直接调用DataUtils.generateLog(700000000,"D:\\Code\\javacode\\LongGeBigData\\data\\ip.log");然后等待五分钟
10GB文件
当然我们也取前100行来做测试:head -n 100 ip.log >> ip.sam.100.log

思路

第一题要求是:一个10G的大文件,每一行加一个行号,并输出到另一个文件上。

思路1

由于题目允许输出到另一个文件,所以我们可以通过BufferedReader和BufferedWriter来处理。BufferedReader读取源文件,BufferedWriter写去另一个文件。很合理。默认private static int defaultCharBufferSize = 8192;

InputStream、OutputStream 处理字节流
InputStreamReader / OutputStreamWriter 处理字符流,将字节流转换为字符流.
BufferedReader BufferedWriter 通用的缓冲方式读写文本

    /**
     * 添加行号
     *
     * @param INPUT_PATH  输入路径
     * @param OUTPUT_PATH 输出路径
     * @throws IOException ioexception
     */
    public static void addLineNum(String INPUT_PATH, String OUTPUT_PATH) throws IOException {
        File fp_in = new File(INPUT_PATH);
        File fp_out = new File(OUTPUT_PATH);
        if (!fp_out.getParentFile().exists()) {
            fp_out.getParentFile().mkdirs();
        }
        fp_out.createNewFile();
        int count = 1;
        try (BufferedWriter bw = new BufferedWriter(new FileWriter(fp_out));
             BufferedReader br = new BufferedReader(new FileReader(fp_in))) {

            String line;
            while((line=br.readLine())!=null){
                if (count % 1000000 == 0)
                    System.out.printf("已经处理了:%d百万个ip了,等等吧\n", count / 1000000);
                bw.write(count++ +" "+ line +'\n');
            }
            bw.flush();
        }
    }

经过demo测试是正确的,所以我们直接跑10GB文件,等一下就知道对不对了。
生成ip.out.log之后,我们看看尾巴tail ip.out.log的行号,以及用wc -l ip.out.log看看是不是一致的就可以了。

(base) ➜  data tail ip.out.log
...
699999998 192.94.89.23
699999999 192.151.29.90
700000000 192.237.193.74

后话

明天完成真正的大文件题目,一个10G的访问IP列表文件,我要统计出现频率TOP 10的IP
方法是:

  1. 生成大文件
  2. 拆分大文件
  3. 小文件处理逻辑
  4. 线程池处理多个小文件
  5. 合并结果输出

明天将一一完成。欢迎留言交流。

2022-3-21 更新

上面的代码有一个问题,如果一行就很大,比如只有一行就10GB,这样会直接爆内存吗?如果会,应该怎么做? 留坑


感谢参考

十道海量数据处理面试题与十个方法总结 【面试+提高】
海量日志中统计次数最多的100个IP

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值