如何用JAVA几分钟处理完30亿个数据?

场景说明

现有一个 10G 文件的数据,里面包含了 18-70 之间的整数,分别表示 18-70 岁的人群数量统计,假设年龄范围分布均匀,分别表示系统中所有用户的年龄数,找出重复次数最多的那个数,现有一台内存为 4G、2 核 CPU 的电脑,请写一个算法实现。

23,31,42,19,60,30,36,........

模拟数据

Java 中一个整数占 4 个字节,模拟 10G 为 30 亿左右个数据, 采用追加模式写入 10G 数据到硬盘里。每 100 万个记录写一行,大概 4M 一行,10G 大概 2500 行数据。

package bigdata;

import java.io.*;
import java.util.Random;

/**
 * @Desc:
 * @Author: bingbing
 * @Date: 2022/5/4 0004 19:05
 */
public class GenerateData {
    private static Random random = new Random();


    public static int generateRandomData(int start, int end) {
        return random.nextInt(end - start + 1) + start;
    }


    /**
     * 产生10G的 1-1000的数据在D盘
     */
    public void generateData() throws IOException {
        File file = new File("D:\ User.dat");
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        int start = 18;
        int end = 70;
        long startTime = System.currentTimeMillis();
        BufferedWriter bos = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file, true)));
        for (long i = 1; i < Integer.MAX_VALUE * 1.7; i++) {
            String data = generateRandomData(start, end) + ",";
            bos.write(data);
            // 每100万条记录成一行,100万条数据大概4M
            if (i % 1000000 == 0) {
                bos.write("\n");
            }
        }
        System.out.println("写入完成! 共花费时间:" + (System.currentTimeMillis() - startTime) / 1000 + " s");
        bos.close();
    }


    public static void main(String[] args) {
        GenerateData generateData = new GenerateData();
        try {
            generateData.generateData();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

上述代码调整参数执行 2 次,凑 10 个 G 的数据在 D 盘的 User.dat 文件里。

准备好 10G 数据后,接着写如何处理这些数据。

场景分析

10G 的数据比当前拥有的运行内存大的多,不能全量加载到内存中读取,如果采用全量加载,那么内存会直接爆掉,只能按行读取,Java 中的 bufferedReader 的 readLine() 按行读取文件里的内容。

读取数据

首先我们写一个方法单线程读完这 30E 数据需要多少时间,每读 100 行打印一次:

private static void readData() throws IOException {

        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(FILE_NAME), "utf-8"));
        String line;
        long start = System.currentTimeMillis();
        int count = 1;
        while ((line = br.readLine()) != null) {
            // 按行读取
//            SplitData.splitLine(line);
            if (count % 100 == 0) {
                System.out.println("读取100行,总耗时间: " + (System.currentTimeMillis() - start) / 1000 + " s");
                System.gc();
            }
            count++;
        }
        running = false;
        br.close();

    }

按行读完 10G 的数据大概 20 秒,基本每 100 行,1E 多数据花 1S,速度还挺快:

处理数据

思路一:通过单线程处理

通过单线程处理,初始化一个 countMap,key 为年龄,value 为出现的次数,将每行读取到的数据按照 "," 进行分割,然后获取到的每一项进行保存到 countMap 里,如果存在,那么值 key 的 value+1。

for (int i = start; i <= end; i++) {
            try {
                File subFile = new File(dir + "\" + i + ".dat");
                if (!file.exists()) {
                    subFile.createNewFile();
                }
                countMap.computeIfAbsent(i + "", integer -> new AtomicInteger(0));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

单线程读取并统计 countMap:

public static void splitLine(String lineData) {
            String[] arr = lineData.split(",");
            for (String str : arr) {
                if (StringUtils.isEmpty(str)) {
                    continue;
                }
                countMap.computeIfAbsent(str, s -> new AtomicInteger(0)).getAndIncrement();
            }
        }

通过比较找出年龄数最多的年龄并打印出来:

private static void findMostAge() {
        Integer targetValue = 0;
        String targetKey = null;
        Iterator<Map.Entry<String, AtomicInteger>> entrySetIterator = countMap.entrySet().iterator();
        while (entrySetIterator.hasNext()) {
            Map.Entry<String, AtomicInteger> entry = entrySetIterator.next();
            Integer value = entry.getValue().get();
            String key = entry.getKey();
            if (value > targetValue) {
                targetValue = value;
                targetKey = key;
            }
        }
        System.out.println("数量最多的年龄为:" + targetKey + "数量为:" + targetValue);
    }

完整代码:

package bigdata;

import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;


/**
 * @Desc:
 * @Author: bingbing
 * @Date: 2022/5/4 0004 19:19
 * 单线程处理
 */
public class HandleMaxRepeatProblem_v0 {

    public static final int start = 18;
    public static final int end = 70;

    public static final String dir = "D:\dataDir";

    public static final String FILE_NAME = "D:\ User.dat";


    /**
     * 统计数量
     */
    private static Map<String, AtomicInteger> 
<think>嗯,用户让我介绍一下时序数据库IOTDB在Java数据采集项目中的作用和应用。首先,我需要理解用户的需求。他们可能在做物联网相关的项目,需要处理时间序列数据,比如传感器数据。用户可能想知道IOTDB如何集成到Java项目中,具体能解决什么问题,或者有什么优势。 首先,我应该解释什么是IOTDB。它是Apache的开源时序数据库,专为物联网设计,处理高并发写入和查询。然后,作用方面,可能需要强调高效存储、查询、处理海量时间序列数据,支持边缘计算和与大数据生态整合。 接下来是应用场景。比如工业物联网中的设备监控,智能家居数据存储,车联网的实时分析。需要具体说明在这些场景中,IOTDB如何被Java应用使用,可能涉及数据采集、存储、分析的流程。 然后,可能需要给出Java中的使用示例,比如如何连接IOTDB,写入数据,查询数据。这样用户能更直观地理解。同时,要提到IOTDB的优势,比如高性能、低存储成本、易扩展,以及支持SQL-like语法,方便集成。 还要考虑用户可能的疑问,比如为什么选择IOTDB而不是其他时序数据库,比如InfluxDB或TimescaleDB。可能需要比较一下IOTDB在物联网场景中的优化,比如针对时间序列的高效压缩算法,或者与Hadoop、Spark的集成能力。 另外,用户可能关心实际项目中遇到的挑战,比如数据写入速度、查询效率、集群部署等。需要说明IOTDB在这些方面的表现,比如水平扩展,高可用性,处理亿级数据点的能力。 可能还需要提到IOTDB的架构,比如支持边缘-中心协同部署,适合分布式物联网环境。这在数据采集项目中可能很重要,尤其是边缘设备资源有限的情况下。 最后,总结一下IOTDB在Java项目中的价值,如何帮助用户高效管理时序数据,提升系统性能,降低存储成本。可能还要提醒一些最佳实践,比如数据分片策略,索引优化,或者结合流处理框架如Flink进行实时分析。 要确保回答结构清晰,先介绍概念,再讲作用,然后应用场景,接着示例代码,最后优势总结。这样用户能逐步了解IOTDB的应用方式。同时,语言要简洁,避免技术术语过多,但关键点要准确。</think>Apache IoTDB 是一款专为物联网场景设计的开源时序数据库,它在 Java 数据采集项目中扮演着**海量时序数据高效存储与实时分析**的核心角色。以下从作用、应用场景和实战示例三个维度为您解析: --- ### 一、IoTDB 在数据采集项目中的作用 1. **高速写入** - 支持每秒百万级数据点写入,满足传感器高频采集需求 - 特有的列式存储+时间编码压缩技术(如Gorilla、RLE),降低存储成本达 90% 2. **时序优化查询** - 原生时间分区索引,毫秒级响应时间范围查询 - 支持降采样、滑动窗口等时序特有计算(`GROUP BY TIME`) 3. **边缘-云协同** - 轻量级架构(核心仅 10MB+JAR)适合部署在边缘设备 - 提供同步工具实现边缘数据向云端汇聚 4. **生态集成** - 无缝对接 Flink/Spark 实时计算,支持 JDBC、MQTT 协议接入 - 兼容 Grafana 可视化,与工业协议(OPC-UA、Modbus)直连 --- ### 二、典型应用场景 #### 1. 工业设备监控 ```java // Java 示例:通过 SDK 写入传感器数据 Session session = new Session("127.0.0.1", 6667); session.open(); String device = "root.factory.machineA"; List<String> sensors = Arrays.asList("temperature", "vibration"); long timestamp = System.currentTimeMillis(); session.insertRecord(device, timestamp, sensors, Arrays.asList(38.5f, 12.7f)); // 写入温度、振动值 ``` #### 2. 智能电网分析 ```sql -- 查询变压器过去1小时每5分钟的平均温度 SELECT AVG(temperature) FROM root.grid.transformers.* WHERE time > NOW() - 1h GROUP BY ([NOW() - 1h, NOW()), 5m) ``` #### 3. 车联网实时预警 ```java // 结合规则引擎进行超速检测 ResultSet result = session.executeQueryStatement( "SELECT speed FROM root.car.* WHERE speed > 120 AND time > 2024-04-01"); while (result.hasNext()) { RowRecord record = result.next(); triggerAlert(record.getDeviceId(), record.getSpeed()); } ``` --- ### 三、架构优势对比 | 特性 | IoTDB | InfluxDB | TimescaleDB | |--------------------|-------------|-------------|-------------| | 原生压缩率 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐ | | 边缘部署能力 | ⭐⭐⭐⭐⭐ | ⭐⭐ | ⭐ | | Hadoop生态兼容性 | 直接集成HDFS| 需额外工具 | 有限支持 | | 工业协议支持 | 内置OPC-UA插件| 无原生支持 | 无 | --- ### 四、最佳实践建议 1. **数据建模** - 使用层级结构组织设备(`root.<区域>.<设备组>.<设备ID>`) - 对高频设备启用时间分片(`CREATE TIMESERIES root.device.* WITH TTL=30d`) 2. **性能调优** - 批量写入时启用`insertTablet`接口提升吞吐 - 调整`wal_buffer_size`防止频繁刷盘 3. **集群部署** - 采用 DataNode+ConfigNode 分离架构实现水平扩展 - 结合 Kubernetes 实现自动扩缩容 --- 通过 IoTDB 的时序数据专精设计,开发者能够构建起从边缘感知到云端分析的数据管道,其 Java 原生接口和丰富的生态工具链,使其成为工业物联网项目的优选数据库方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值