Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/postgresql/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
HikariCP的Postgresql性能问题_Postgresql_Jdbc_Hikaricp - Fatal编程技术网

HikariCP的Postgresql性能问题

HikariCP的Postgresql性能问题,postgresql,jdbc,hikaricp,Postgresql,Jdbc,Hikaricp,我正在尝试以小批量(每个csv中有6000行)将大数据加载到PostgreSQL server中的一个表中(总共4000万行)。我认为HikariCP是实现这一目标的理想选择 这是我使用 Java 8(1.8.0_65)、Postgres JDBC驱动程序9.4.1211和HikariCP 2.4.3 4分42秒内排6000行。 我做错了什么?如何提高插入速度? 关于我的设置,请多说几句: 该程序在我的笔记本电脑上运行,支持公司网络 Postgres服务器9.4是Amazon RDS,带有db

我正在尝试以小批量(每个csv中有6000行)将大数据加载到PostgreSQL server中的一个表中(总共4000万行)。我认为HikariCP是实现这一目标的理想选择

这是我使用 Java 8(1.8.0_65)、Postgres JDBC驱动程序9.4.1211和HikariCP 2.4.3

4分42秒内排6000行。

我做错了什么?如何提高插入速度?

关于我的设置,请多说几句:

  • 该程序在我的笔记本电脑上运行,支持公司网络
  • Postgres服务器9.4是Amazon RDS,带有db.m4.large和50 GB SSD
  • 尚未在表上定义显式索引或主键
  • 程序使用大线程池异步插入每一行,以容纳请求,如下所示:

    private static ExecutorService executorService = new ThreadPoolExecutor(5, 1000, 30L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(100000));
    
    这是我读取源数据的地方:

        private void readMetadata(String inputMetadata, String source) {
                BufferedReader br = null;
                FileReader fr = null;
                try {
                    br = new BufferedReader(new FileReader(inputMetadata));
                    String sCurrentLine = br.readLine();// skip header;
                    if (!sCurrentLine.startsWith("xxx") && !sCurrentLine.startsWith("yyy")) {
                        callAsyncInsert(sCurrentLine, source);
                    }
                    while ((sCurrentLine = br.readLine()) != null) {
                        callAsyncInsert(sCurrentLine, source);
                    }
                } catch (IOException e) {
                    LOG.error(ExceptionUtils.getStackTrace(e));
                } finally {
                    try {
                        if (br != null)
                            br.close();
    
                        if (fr != null)
                            fr.close();
    
                    } catch (IOException ex) {
                        LOG.error(ExceptionUtils.getStackTrace(ex));
                    }
                }
        }
    
    我正在异步插入数据(或尝试使用jdbc!):

    在pgAdmin4中进行监控时,我注意到了以下几点:

    • 每秒的最高事务数接近50
    • 活动数据库会话只有一个,会话总数为15个
    • 块I/O太多(达到500左右,不确定是否会引起问题)

    您绝对希望使用批处理插入,语句在循环之外准备,并且自动提交关闭。在伪代码中:

    PreparedStatement stmt = conn.prepareStatement("insert into xyz(...) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")
    while ( <data> ) {
       stmt.setString(1, column1);
       //blah blah blah
       stmt.addBatch();
    }
    stmt.executeBatch();
    conn.commit();
    
    PreparedStatement stmt=conn.prepareStatement(“插入xyz(…)值(?,,,,,,,,,,,,,,,,,))
    而(){
    stmt.setString(1,第1列);
    //废话废话
    stmt.addBatch();
    }
    stmt.executeBatch();
    conn.commit();
    
    即使是单个连接上的单个线程也应该能够每秒插入>5000行

    更新:如果要对其进行多线程处理,则连接数应为数据库CPU核心数x1.5或2。处理线程的数量应该与之匹配,每个处理线程应该使用上面的模式处理一个CSV文件。但是,您可能会发现,同一个表中的许多并发插入会在数据库中产生太多的锁争用,在这种情况下,您需要减少处理线程的数量,直到找到最佳并发性

    一个适当大小的池和并发性应该很容易达到>20K行/秒


    另外,请升级到HikariCP v2.6.0。

    减少连接池的大小和使用的线程数:更多的连接(和更多的线程)并不一定会带来更好的性能,甚至有一点(可能远低于当前设置)需要更多的连接(和线程)实际上会导致性能和吞吐量的降低。此外,您还应该关闭方法中的连接,将其返回到连接池以供重用。此外,您是否确实检查了瓶颈是否与异步插入有关,可能问题在于您没有显示的代码(调用
    callasyncsert
    )。感谢您的回复:将连接池和线程数减少到10。另外,插入后关闭连接(关闭ConnectionProxy对象)。我称之为callAsyncInsert的工作并不繁重,只需读取csv并将其传递给callAsyncInsert即可。进行这些更改后,仍需4分42秒。有什么想法吗?您可以尝试不异步执行,而是使用批插入?显示调用
    callAsyncInsert
    的代码?还要了解,从笔记本电脑连接到AWS上托管的数据库可能会有相当大的延迟。您在本地数据库上测试过吗?多线程导入的线程数量不仅取决于服务器上的CPU数量,还取决于该服务器上的硬盘数量。@a_horse_,带有_no_名称,如果为true,则使用Amazon RDS,则无法知道该数量。确定。我已根据建议修改了程序。升级到2.6.0。添加了批插入并仅使用连接加载数据。现在我看到了两种不同类型的数据集之间的巨大差异。数据集#1是一个csv文件中的500K行(精确地说是499951)-00:02:08.670分钟。数据集#2在83个CSV文件中为498K,每6K行花费00:02:09.674分钟。所以我可以得到每秒3840ish的吞吐量。如果我没有宏日志、错误处理等沉重的框架,我可能会得到更多,但我对此感到高兴。非常感谢Woolridge先生的这个框架和Mark的帮助。
                private void callAsyncInsert(final String line, String source) {
                        Future<?> future = executorService.submit(new Runnable() {
                            public void run() {
                                try {
                                    dataLoader.insertRow(line, source);
                                } catch (SQLException e) {
                                    LOG.error(ExceptionUtils.getStackTrace(e));
                                    try {
                                        errorBufferedWriter.write(line);
                                        errorBufferedWriter.newLine();
                                        errorBufferedWriter.flush();
                                    } catch (IOException e1) {
                                        LOG.error(ExceptionUtils.getStackTrace(e1));
                                    }
                                }
                            }
                        });
                        try {
                            if (future.get() != null) {
                                LOG.info("$$$$$$$$" + future.get().getClass().getName());
                            }
                        } catch (InterruptedException e) {
                            LOG.error(ExceptionUtils.getStackTrace(e));
                        } catch (ExecutionException e) {
                            LOG.error(ExceptionUtils.getStackTrace(e));
                        }
                    }
    
                public void insertRow(String row, String source) throws SQLException {
                        String[] splits = getRowStrings(row);
                        Connection conn = null;
                        PreparedStatement preparedStatement = null;
                        try {
                            if (splits.length == 15) {
                                String ... = splits[0];
                                //blah blah blah
    
                                String insertTableSQL = "insert into xyz(...) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?) ";
                                conn = getConnection();
                                preparedStatement = conn.prepareStatement(insertTableSQL);
                                preparedStatement.setString(1, column1);
                                //blah blah blah
                                preparedStatement.executeUpdate();
                                counter.incrementAndGet();
                                //if (counter.get() % 1000 == 0) {
                                    //conn.commit();
                                //}
                            } else {
                                LOG.error("Invalid row:" + row);
                            }
                        } finally {
                            /*if (conn != null) {
                                conn.close();   //Do preparedStatement.close(); rather connection.close
                            }*/
                            if (preparedStatement != null) {
                                preparedStatement.close();
                            }
                        }
                    }
    
    PreparedStatement stmt = conn.prepareStatement("insert into xyz(...) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")
    while ( <data> ) {
       stmt.setString(1, column1);
       //blah blah blah
       stmt.addBatch();
    }
    stmt.executeBatch();
    conn.commit();