【问题标题】:Slow insert on PostgreSQL using JDBC使用 JDBC 在 PostgreSQL 上缓慢插入
【发布时间】:2016-10-22 20:42:09
【问题描述】:

我在一个系统上工作,该系统将数据从云系统下载到本地数据库(PostgreSQL、MySQL、...)。现在我遇到了 PostgreSQL 性能问题,因为插入数据需要很长时间。

列数和数据大小可能会有所不同。在一个示例项目中,我有一个大约有一个表。 170 列。有一个唯一索引 - 但即使删除索引后插入速度也没有改变。

我正在使用 JDBC 驱动程序连接到数据库,并且我正在以 250 行为单位批量插入数据(使用 NamedParameterJdbcTemplate)。

我花了大约。 在 Postgres 上插入数据需要 18 秒在 MySQL 上的相同数据集只花了我一秒钟。这是一个巨大的差异——它来自哪里? Postgres JDBC 驱动程序有那么慢吗?可以以某种方式对其进行配置以使其更快吗?我还缺少其他东西吗? Postgres 和 MySQL 之间的差异是如此巨大。任何其他想法如何使它更快?

我在 Github - https://github.com/varad/postgresql-vs-mysql 上创建了一个示例项目。一切都发生在 LetsGo class 中的 "run" 方法中。

【问题讨论】:

  • 您可以启用更多调试以查看实际速度慢吗? (插入、提交、连接)?用于在 java 中调试(用于驱动程序) loglevel=2 (jdbc.postgresql.org/documentation/80/connect.html) 用于服务器端日志记录 drupal.org/node/560192
  • 我们在谈论哪些版本?您是否尝试过交易?
  • 另外,您是否尝试过反转并首先将其插入 MySQL 并查看会发生什么(例如 letsGo.run(Type.MYSQL); letsGo.run(Type.POSTGRES);)?还有你是怎么看时间的?
  • 请你这样做:create table t1 as select * from your_table limit 250; 然后pg_dump --inserts -t t2 到一个文件,然后尝试在psql 测量时间(\timing on 开关)运行文件 - 这会给你预期insert 的机器上 250 行的速度。然后创建索引等。并再次测量。
  • 启用语句记录时,我可以看到该语句大约准备好了。在实际插入开始之前 20.000(是的 20 )次。一旦实际插入开始,只需要大约。 1秒。但是 20.000 条准备语句在我的计算机上需要大约 30 秒。我不知道这是由 Spring 还是某些配置选项引起的。

标签: java postgresql jdbc


【解决方案1】:

这似乎是 Spring“错误”和驱动程序“错误”的组合。

每次调用setValue() 时,Spring 都会尝试确定列的数据类型。它通过调用PreparedStatementMetaData.getParameterMetaData()

来实现

这显然会导致将“准备”语句发送到数据库,该语句本身非常快(在我的笔记本电脑上从不超过 1 毫秒),但因为它是为每一行的每个 column 调用的这总共需要很长时间(每个非空值都会调用它,这会导致大约 23.000 次调用)

在某种程度上,这更像是一个 Spring 错误而不是驱动程序错误,因为不缓存参数元数据实际上没有意义(至少在我看来)。 MySQL JDBC 驱动程序不支持getParameterMetaData(),Spring 知道这一点,因此这个“错误”不会出现在 MySQL 中,因为 spring 从不调用该方法。

我不确定 Postgres 的 JDBC 驱动程序行为是否可以归类为错误,但如果驱动程序在第一次调用后缓存该元数据肯定会很好。

可以说服Spring不要通过属性spring.jdbc.getParameterType.ignore获取语句元数据

所以说:

System.setProperty("spring.jdbc.getParameterType.ignore", "true");

行:

LetsGo letsGo = new LetsGo();

此行为已禁用。

该属性必须在 Spring 初始化之前设置。

当我对您的示例项目执行此操作时,插入在我的笔记本电脑上运行时间为 500 毫秒。


编辑

在看到有关使用 Postgres-NG 驱动程序的评论后,我挖掘了“官方”驱动程序和 NG 驱动程序的来源,NG 驱动程序在第一次调用后确实缓存了参数元数据,而官方驱动程序并没有解释为什么使用 NG 驱动程序要快得多(没有在 Spring 中禁用调用)

【讨论】:

  • 谢谢!我相信这是一个完美的答案。虽然如果我设置属性我得到“DataIntegrityViolationException”不知道为什么。除了添加属性之外,您是否需要更改其他内容?
  • @varad:不,那是我唯一改变的。
  • 有趣的是,我收到“22P02:整数的无效输入语法”。但我想这是其他类型的问题。
  • 我想通了。解析我的 XML 并将值转换为 Java 类型的代码将 Integer 设置为 Text 字段,因此它失败了。所以它根本与这个 Stack Overflow 问题无关。好消息是启用该参数后它真的很快!感谢您调查该问题,这真的很有帮助!
【解决方案2】:

尝试使用 pgjdbc-ng 驱动程序,然后比较你的结果。

这里有: http://impossibl.github.io/pgjdbc-ng/

【讨论】:

  • 哇,有了这个驱动程序,速度超级快。只需半秒!
【解决方案3】:

我希望您使用的是数据库连接池。你可以试试C3P0。 Spring (JDBCTemplate) 不提供连接池实现。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-05-25
    • 2018-03-30
    • 2010-11-15
    • 1970-01-01
    • 2012-03-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多