【问题标题】:Java Database Handling Performance SuggestionJava 数据库处理性能建议
【发布时间】:2017-02-15 13:18:54
【问题描述】:

我正在研究从 oracle 数据库表中读取记录并在读取每条记录后进行一些处理的 Java 应用程序。 注意:应用程序只从数据库中读取数据,它不会写回任何东西。

我正在使用标准的 jdbc 驱动程序、连接、准备好的语句和结果集。 我需要获取的记录数约为 500000(最大)。

我目前的实现:

我已将语句提取大小设置为 500。(查询是一个表中的简单选择语句) 在读取每个结果集记录时,我将每个记录添加到一个 java 集合中,如下所示:

        List<HashMap<String,String>> userData=new ArrayList<HashMap<String,String>>();
        ResultSetMetaData resultSetMetData = resultSet.getMetaData();
        while(resultSet.next()){
            HashMap<String,String> recordMap = new HashMap<String,String>();
            for (int i = 1; i <= resultSetMetData.getColumnCount(); i++) {
                String key = resultSetMetData.getColumnName(i);
                String value = resultSet.getString(key);
                recordMap.put(key, value);
            }
            userData.add(recordMap);
        }

将所有行添加到 java 集合后,我将关闭连接。

我希望您能就最有效的方法提出建议。如果您认为这样更好,我愿意编写多线程事件。

谢谢你,
腰带

【问题讨论】:

  • 如果您将所有 200000 行保存在 List 中,它将占用大量内存。你用数据做什么?不能用你需要的格式SELECT'ed吗
  • @ScaryWombat 不行。我需要根据每条记录做一些处理。如果在列表中添加所有行会占用大量内存,建议将结果集保持打开这么长时间,直到我处理完所有记录?
  • 尽管我说memory intensive 这对你来说可能不是问题。你真的遇到什么问题了吗?
  • @ScaryWombat 到目前为止还不是真正的问题,但我们的应用程序还没有上线。我只是想确保我使用了正确的方法。一个问题:如果我将 fetch size 设置为 500,并且在读取每 500 条记录后,我生成一个线程来处理 500 条记录,你认为这会是一个优势还是变得复杂?
  • 需要做什么样的处理?如果您可以将处理逻辑移动到数据中,而不是移动所有数据呢?

标签: java oracle performance


【解决方案1】:

你也应该处理你的收藏。

1- 如果您事先知道您的列表会很大,您可以使用ArrayList Capacity

默认情况下,ArrayList 的初始容量为 10 条记录。当您的列表增长时,java 会不断地(小)重新分配列表,从而浪费大量时间。如果你这样做,例如

  List<HashMap<String,String>> userData=new ArrayList<HashMap<String,String>>(500);

你将有一个 500 的 initialCapacity,节省了大量的重新分配,所以时间。

ensureCapacity 方法也很有用,它可以让您的列表增长到用户定义的容量。

因此,如果您事先知道结果的大小,或者至少您有一个想法,您就可以使用它。

否则,如果之后只需要对列表进行迭代,则可以考虑LinkedList,它对于添加操作具有恒定的时间(但对于位置访问没有)。

2- 也考虑一下你的行数据结构,创建一个封装你的结果集列的对象,而不是为每一行创建一个映射(所以很多映射,每个映射都需要一定的内存分配,所以资源和时间),效率会更高。

查看 CollectionFrameworkArrayListLinkedList 文档

更新 这里有一些“肮脏”的基准

package com.test;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

public class LoopTest {

    public static void main(String[] args) {
        LoopTest lt=new LoopTest();
        int records = 200000;
        lt.execute(records, false);
        lt.execute(records, true);
        lt.executeObj(records, false);
        lt.executeObj(records, true);
        lt.executeReflect(records, false);
        lt.executeReflect(records, true);


    }

    public void execute(int loops, boolean useCapacity){
        Date start=new Date();
        System.out.println("EXAMPLE WITH HASHMAP");
        System.out.println(start+ " time: "+start.getTime()+" Start for loops="+loops+ " and useCapacity="+useCapacity);
        List<HashMap<String,String>> userData=null;
        if(useCapacity)
            userData=new ArrayList<HashMap<String,String>>(loops);
        else 
            userData=new ArrayList<HashMap<String,String>>();

        for(int i=0;i<loops;i++){
            HashMap<String,String> recordMap = new HashMap<String,String>();
            for (int j = 1; j <= 10; j++) {
                String key = j+"";
                String value = j+" val";
                recordMap.put(key, value);
            }
            userData.add(recordMap);
        }
        Date end=new Date();
        System.out.println(end+ " time: "+end.getTime()+", elapsed="+(end.getTime()-start.getTime())+" end for loops="+loops+ " and useCapacity="+useCapacity);
        System.out.println("-------------------------");

    }

    public void executeObj(int loops, boolean useCapacity){
        System.out.println("EXAMPLE WITH CLASSIC OBJECT");
        Date start=new Date();
        System.out.println(start+ " time: "+start.getTime()+" Start for loops="+loops+ ", object and useCapacity="+useCapacity);
        List<TestObj> userData=null;
        if(useCapacity)
            userData=new ArrayList<TestObj>(loops);
        else 
            userData=new ArrayList<TestObj>();

        for (int i=0;i<loops;i++){
            TestObj testObj = new TestObj();
            testObj.test1="1";
            testObj.test2="1";
            testObj.test3="1";
            testObj.test4="1";
            testObj.test5="1";
            testObj.test6="1";
            testObj.test7="1";
            testObj.test8="1";
            testObj.test9="1";
            testObj.test10="1";
            testObj.test11="1";
            testObj.test12="1";
            testObj.test13="1";
            testObj.test14="1";
            testObj.test15="1";
            testObj.test16="1";
            testObj.test17="1";
            testObj.test18="1";
            testObj.test19="1";
            testObj.test20="1";
            userData.add(testObj);
        }
        Date end=new Date();
        System.out.println(end+ " time: "+end.getTime()+", elapsed="+(end.getTime()-start.getTime())+" end for loops="+loops+ ", object and useCapacity="+useCapacity);
        System.out.println("-------------------------");

    }

    public void executeReflect(int loops, boolean useCapacity){
        System.out.println("EXAMPLE WITH REFLECTION");
        Date start=new Date();
        System.out.println(start+ " time: "+start.getTime()+" Start for loops="+loops+ ", object and useCapacity="+useCapacity);
        List<TestObj> userData=null;
        if(useCapacity)
            userData=new ArrayList<TestObj>(loops);
        else 
            userData=new ArrayList<TestObj>();



        for (int i=0;i<loops;i++){
            try{
                Class<?> objClass=Class.forName("com.test.TestObj");
                Object myObj=objClass.newInstance();


            for(int j=1;j<=20;j++){

                Field f=objClass.getDeclaredField("test"+j);
                f.set(myObj, "1");
            }
            userData.add((TestObj)myObj);

            } catch (ClassNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (InstantiationException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (SecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }



        }
        Date end=new Date();
        System.out.println(end+ " time: "+end.getTime()+", elapsed="+(end.getTime()-start.getTime())+" end for loops="+loops+ ", reflect and useCapacity="+useCapacity);
        System.out.println("-------------------------");

    }


}

TestObj 是一个仅包含名为 test1...test25 的公共字段的对象。为了简单和快速,我将它们公开,在现实世界中你会将它们设为私有。它可以进行更多设计,但我很快就完成了..

这里是阐述的输出

EXAMPLE WITH HASHMAP, useCapacity=false, loops=200000
Wed Feb 15 06:04:48 CET 2017 time: 1487135088903 Start for loops=200000 and useCapacity=false
Wed Feb 15 06:04:55 CET 2017 time: 1487135095922, elapsed=7019 end for loops=200000 and useCapacity=false
-------------------------
EXAMPLE WITH HASHMAP, useCapacity=true, loops=200000
Wed Feb 15 06:04:55 CET 2017 time: 1487135095922 Start for loops=200000 and useCapacity=true
Wed Feb 15 06:05:01 CET 2017 time: 1487135101073, elapsed=5151 end for loops=200000 and useCapacity=true
-------------------------
EXAMPLE WITH CLASSIC OBJECT, useCapacity=false, loops=200000
Wed Feb 15 06:05:01 CET 2017 time: 1487135101073 Start for loops=200000, object and useCapacity=false
Wed Feb 15 06:05:01 CET 2017 time: 1487135101254, elapsed=181 end for loops=200000, object and useCapacity=false
-------------------------
EXAMPLE WITH CLASSIC OBJECT, useCapacity=true, loops=200000
Wed Feb 15 06:05:01 CET 2017 time: 1487135101254 Start for loops=200000, object and useCapacity=true
Wed Feb 15 06:05:01 CET 2017 time: 1487135101274, elapsed=20 end for loops=200000, object and useCapacity=true
-------------------------
EXAMPLE WITH REFLECTION, useCapacity=false, loops=200000
Wed Feb 15 06:05:01 CET 2017 time: 1487135101274 Start for loops=200000, object and useCapacity=false
Wed Feb 15 06:05:05 CET 2017 time: 1487135105562, elapsed=4288 end for loops=200000, reflect and useCapacity=false
-------------------------
EXAMPLE WITH REFLECTION, useCapacity=true, loops=200000
Wed Feb 15 06:05:05 CET 2017 time: 1487135105562 Start for loops=200000, object and useCapacity=true
Wed Feb 15 06:05:09 CET 2017 time: 1487135109711, elapsed=4149 end for loops=200000, reflect and useCapacity=true
-------------------------

正如您将看到的,使用容量将哈希图示例从大约 7 秒缩短到 5.1 秒。

以经典方式使用对象可以达到 181 毫秒 (!) 甚至 20 毫秒 (!!) 使用数组中的容量。

反射性能与容量的使用无关,大约需要 4 秒(4288 对 4149)。

请注意,基准测试的确切时间可能因执行而异。但总的来说时间顺序总是一样的。

关于以“块”形式获取记录,这是一个很好的内存一致性解决方案,尤其是在您的阐述可能很长的情况下。通常长时间运行的查询可能会出现“快照太旧”错误,这可能会产生问题。而且也不确定性能会因此而下降很多。 在过去,我必须处理类似的事情,一个非常好的解决方案是在源表中放置一个带有索引的字段“chunk_id”,这样可以轻松地重复我对“下一个”行的查询,并给了我更多效率高于检索表的所有内容。一般来说,您只需要一种方法来识别您的记录组,例如第 1 组、第 2 组、.. 第 n 组(当然,还要在其中放置索引。

PS反射的例子很简单,但这只是为了展示如何做,你可以使用很多特性,包括方法等

【讨论】:

  • 感谢您的回答。关于第 2 点。我可以创建一个对象来封装我的结果集列。但我有一个问题。目前,我从 DB 获取的列是可配置的,如果我有一个对象,我将失去对 的灵活性
  • 在这种情况下,我相信你不能这样做。但这当然取决于您可以拥有多少配置。你的桌子真的变化很大吗?
  • 其实永远不会改变,只是配置映射可能会改变,总之我需要将表列映射到一些内部字段,这就是配置,如果我使用对象我会失去这种灵活性跨度>
  • 您也可以使用反射将对象字段映射到那些内部字段。这可能会带来一些性能劣势(但可能比分配成千上万的映射要小得多),并且更具程序性努力。也许你可以做一个基准测试。如果今晚我有时间,我会给你举个例子
  • 谢谢伙计!我将寻找映射字段的反射。如果你有时间,请给我一些链接或一个小例子,谢谢!
【解决方案2】:

这个问题的答案取决于您的 SQL 查询与您需要进行的 API 调用相比的相对速度,以及您拥有的内存量与记录的大小和数量相比。

不知道这些,很难知道。

如果 API 调用速度相对较慢并且您有足够的内存:

  1. 创建一个ThreadPoolExecutor,其中包含您调整的多个线程
  2. 查询您的数据库,并将每一行一一发送以由执行器执行

如果查询比较慢或者你的内存很少:

  1. 将您的 select 语句划分为 8 个查询,每个查询返回数据的不同部分
  2. 创建 8 个线程
  3. 每个线程运行一个查询并在 Oracle 返回每一行时调用 API

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-09-13
    • 1970-01-01
    • 2012-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-13
    • 2010-10-17
    相关资源
    最近更新 更多