如何快速插入100,000个父行以及200个子行?

时间:2019-03-29 13:53:12

标签: java database jpa

我有一个称为OrderEvent的父实体和一个称为PreCondition的子实体。一个OrderEvent可以具有多个PreConditions(> = 200)。我需要保存100000 OrderEvent + 100000 * 200前提条件。我使用Repository.save(OrderEvents列表)并每1000条记录保存到DB中。插入1000个OrderEvents大约需要30秒。

保存所有100000个OrderEvent大约需要一个小时。

有什么办法可以降低2分钟以下的时间吗?

尝试了存储库的保存实体方法

    public  void parseOrder(String path, String collectionName) throws ParseException {
        BufferedReader reader;
        Connection conn = (Connection) em.unwrap(java.sql.Connection.class);
        System.out.println(conn);
        try {
            reader = new BufferedReader(new FileReader(
                    path));
            String line = reader.readLine();

            String jobNumber =  line.substring(0, 7).trim();
            String recordType =  line.substring(7, 9).trim();
            Integer len = line.length();
            preId = 0L;
            postId = 0L;
            eventId = 0L;

            OrderEvent orderEvent = this.paraseHeader(line,len,jobNumber,collectionName);
            Integer count = 1;
            Integer batch = 0;
            long startTime = System.nanoTime();

            List<OrderEvent> list = new ArrayList<OrderEvent>();
            while (line != null) {
                line = reader.readLine();
                if (line == null) {
                    continue;
                }
                jobNumber =  line.substring(0, 7).trim();
                recordType =  line.substring(7, 9).trim();
                len = line.length();

                if (recordType.equals("0H")) { 

                    count++;
                    batch++;
                    if (batch.equals(1000)) {
                        orderRepository.save(list);
                        list.clear();
                        long estimatedTime = System.nanoTime() - startTime;
                        System.out.println("Processed " +  batch + " records in " +  estimatedTime / 1_000_000_000.  +  " second(s).");

                        batch = 0;
                        startTime = System.nanoTime();
                    }


                    list.add(orderEvent);
                    //orderRepository.saveAndFlush(orderEvent);
                    orderEvent = this.paraseHeader(line,len,jobNumber,collectionName);

                } else if (recordType.equals("2F")) { 
                    this.paraseFeature(line,len,jobNumber,orderEvent);
                }
            }
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    private  OrderEvent paraseHeader (String line,Integer len,String jobNumber,String collectionName) throws ParseException {

            String model = line.substring(9, 16).trim();
            String processDate =  line.substring(len-11,len-3).trim();
            String formattedProcessDate =  processDate.substring(0,4) + "-" + 
                    processDate.substring(4,6) +"-" + processDate.substring(6,8) + " 00:00:00";

            //eventId++;

            OrderEvent orderEvent = new OrderEvent(jobNumber,UUID.randomUUID().toString(),collectionName,
                    formatter.parse(formattedProcessDate));

        //  preId++;
            //postId++;
            orderEvent.fillPrecondition("Model", "Stimulus", "OP_EQ", model);
            orderEvent.fillPostcondition("Add_Fact","Coded","Response","True");


            return orderEvent;
    }
    private  void paraseFeature (String line,Integer len, String jobNumber, OrderEvent orderEvent) {

    //  preId++;
        String feature = line.substring(len-7,len).trim();
        orderEvent.fillPrecondition("Feature", "Stimulus", "OP_EQ", feature);
    }

3 个答案:

答案 0 :(得分:2)

这通常取决于数据库设置,例如客户端的等待时间是多少,表上的索引是什么,查询如何锁定表等等。

确保您了解在网络操作上花费了多少时间。这可能是限制因素,尤其是如果您的数据库位于世界的另一端。

首先确定客户端和数据库服务器之间的延迟是多少。如果是10毫秒,则逐行插入此代码将是:100,000 * 200 * 10毫秒= 200000s〜56h。这非常慢,因此请确保将批处理插入与JDBC一起使用。

有时,通过创建影子表可以大大加快插入过程:

  1. 创建与OrderEventsPreCondition表相同的新表。某些RDBMS允许使用CREATE TABLE ... AS SELECT ... FROM ...语法。
  2. 在影子表上禁用外键和索引。
  3. 批量插入所有数据。
  4. 在影子表上启用外键和索引。希望这可以确保导入的数据是正确的。
  5. 将影子表插入到实际表中,例如通过运行INSERT INTO ... SELECT ... FROM ...
  6. 删除影子表。

不过,最好的选择是跳过JDBC并切换到数据库提供的批量加载实用程序,例如Oracle DB具有External TablesSQL*Loader。这些工具经过专门设计,可以有效地吸收大量数据,而JDBC是通用接口。

答案 1 :(得分:0)

使用DB服务器的BULK处理操作更好地做到这一点。 是的,这是完全不同的过程,但是将需要几秒钟。甚至没有几分钟。

不幸的是,HOWTO非常依赖于SQL Server

MS SQL:批量插入:https://docs.microsoft.com/en-us/sql/t-sql/statements/bulk-insert-transact-sql?view=sql-server-2017

PostgreSQL:复制:https://www.postgresql.org/docs/current/sql-copy.html

答案 2 :(得分:0)

在c#中,我可以将SqlBulkCopy用于此类任务。

也许在Java中有一个等效的API。 像这样的东西:com.microsoft.sqlserver.jdbc.SQLServerBulkCopy