有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java构建大型文件的动态查询

我试图加载一个大的文本文件(介于400-800MB之间),并且对于要将记录插入数据库的文件,但是我遇到了性能问题和内存问题(没有足够的堆空间)。我想知道从我目前的工作中是否有更好的方法

因此,我正在加载的文本文件具有简单的格式,它类似于:

00000  Andy   8920  N  UNL  ...
00001  Roger  4428  N  TRX  ...
,,, 

当前方法:读取每一行,获取字段,然后构建查询

ArrayList<ArrayList<String>> fields = ArrayList<ArrayList<String>>();
ArrayList<String> data= new ArrayList<String>();
while ((line = br.readLine()) != null) {
    if(line.length() >= 6)
        data.add(line.substring(0, 6)); 
    if(line.length() >= 24)
        data.add(line.substring(6, 15));  
    if(line.length() >= 30)
        data.add(line.substring(15, 20)); 
    if(line.length() >= 48)
        data.add(line.substring(20, 25));
...
    fields.add(data); //it looks like [[00000, Andy   , 8920,..],[00001, Roger, ...]]
} //end read
System.gc();
db.insertValues(input);

DB代码

public void insertValues(ArrayList<ArrayList<String>> data) {
        PreparedStatement ps = null;
        Connection con = null;
        try {
            con = getConnection();
            ps = con.prepareStatement("Insert into CUST_ACCT "
                    + "(CID,NAME,R_NUM,CKM_IND,DATE_1,DATE_2,DATE_3,DATE_4,DATE_5,DATE_6,DATE_7,DATE_8,DATE_9,DATE_10,NUMBER_1,NUMBER_2,NUMBER_3,NUMBER_4,NUMBER_5,NUMBER_6,NUMBER_7,NUMBER_8,NUMBER_9,NUMBER_10,STRING_1,STRING_2,STRING_3,STRING_4,STRING_5,STRING_6,STRING_7,STRING_8,STRING_9,STRING_10,GUID,PARN_GUID,LAST_UPDT_DATE_TIME_STAMP)"
                    + " values "
                    + "(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,sysdate)");
for(int i=0; i< data.size(); i++) {
                ps.setString(1, data.get(i).get(0)); //0
                ps.setString(2, data.get(i).get(1)); //1
                ps.setString(3, data.get(i).get(2)); //2
                ps.setString(4, data.get(i).get(3)); //3
        ...
        ps.addBatch();
        }
        int[] i = ps.executeBatch();
        log.info("total of record inserted: "+i.length);
    }

然而,我得到了Not enough heap space错误,我也尝试构建查询,但它会一个接一个地插入记录,一个小时后它只会插入数百万条记录中的大约20k条。是否有更好的方法加载数据


共 (3) 个答案

  1. # 1 楼答案

    将所有文件加载到内存中,然后尝试逐行读取所有文件,这会导致性能和内存问题(堆空间等)

    您可以使用Scanner读取文件,这样就可以逐行读取,而无需加载到内存中

    FileInputStream inputStream = null;
    Scanner sc = null;
    try {
        inputStream = new FileInputStream(path);
        sc = new Scanner(inputStream, "UTF-8");
        while (sc.hasNextLine()) {
            String line = sc.nextLine();
            // db insert!
        }
        if (sc.ioException() != null) {
            throw sc.ioException();
        }
    } finally {
        if (inputStream != null) {
            inputStream.close();
        }
        if (sc != null) {
            sc.close();
        }
    }
    

    否则使用Apache Commons IO

    LineIterator it = FileUtils.lineIterator(theFile, "UTF-8");
    try {
        while (it.hasNext()) {
            String line = it.nextLine();
            // do something with line
            // db insert
        }
    } finally {
        LineIterator.closeQuietly(it);
    }
    

    为了更好的表现,我建议你只打开一次连接

       // your logic....
       Connection con = getConnection();
       // reading file logic
       while (it.hasNext()) {
            String line = it.nextLine();
            // do something with line
            insertValues(con, line);
            // other logic
       }
       // checking exception etc
       } finally {
            if (inputStream != null) {
                inputStream.close();
            }
            if (sc != null) {
                sc.close();
            }
    
            if (con != null ) {
                con.close();
            }
    
        }
    

    总结起来:

    1. 逐行读取文件而不加载到内存中
    2. 只打开连接一次(或几次,而不是每次插入)
    3. 将连接对象传递给插入方法
    4. 完成后关闭一切

    希望你能理解。。。这些都是简单的例子,你需要根据自己的需要来选择它们

  2. # 2 楼答案

    让我看看我是否正确理解您的需求:

    您有一个大文件,文件中的每一行都需要插入数据库中的更多表。我理解对了吗

    如果是,您是否尝试过使用Oracle的"SQL*Loader"工具? 我没有测试这么大的文件,但它可能是一个解决方案。 你可以通过Java应用程序调用它

  3. # 3 楼答案

    不要读取整个文件——读取1000行,然后使用准备好的语句插入它们,然后提交事务。然后再读1000

    此外,我认为Oracle有一个加载数据的特殊工具(google SQL*Loader和data pump)