【问题标题】:Best way to optimize DB inserts using Java使用 Java 优化数据库插入的最佳方法
【发布时间】:2021-04-27 18:41:26
【问题描述】:

程序是用java编写的。 25 个线程正在处理 100 万个任务。每个任务都将数据保存到数据库中,因此数据库插入发生了 100 万次。为了优化这一点,我们尝试了以下方法

  1. 任务将数据保存到 ConcurrentLinkedDeque
  2. 线程定期轮询 duque 并获取该时间点的所有可用对象。
  3. 一旦可用对象的计数达到阈值(例如 100K),然后创建一个线程进行保存。

但这种方法并没有提高整体性能。

我想减少数据库插入的次数(目前为 100 万次)以提高性能。是否有任何替代解决方案,例如高性能 - 多个并发发布者和单个并发订阅者类型的实现?

【问题讨论】:

  • 1.你应该使用baeldung.com/jdbc-batch-processing。做一些实验来选择最佳批量大小 2. 插入/更新的性能很大程度上取决于数据库的东西,如表大小、使用的数据类型、索引、外键等约束、触发器等。您可能需要优化数据库/表以进行插入。一个经典的技巧是在大量加载之前删除索引,并在加载所有数据后恢复它们。通常,您可能期望一个线程(对于一个单个 HDD)每秒最多 5k 次插入。 3. 试验多少线程给你带来最高性能的加载 1-10..-50?
  • 另外,你应该检查你的算法。 “池......在周期性间隔中”听起来您可能会引入额外的延迟/吞吐量限制......如果您使用小超时进行“轮询”以填充新批次怎么办?顺便说一句,你为什么需要另一个线程来保存,为什么不保存在池线程中?无论如何,您应该从了解您的限制开始 - 对于您的特定数据库/表,您可以在一个线程(带有测试循环)中实现多少次插入。选择正确的批量大小。然后运行更多线程,看看有什么不同...
  • @AnatolyG,感谢您的 cmets。表是分区的,每个分区有 100+ 百万条记录。因此,在插入时删除索引并重新添加索引将不起作用。轮询线程没有保存,因为我想提高性能。在轮询和累积记录时,如果达到阈值(比如 30k 条记录),我会生成一个线程来保存。这种方法也没有产生良好的性能。
  • 您是否尝试在没有任何索引和任何限制的情况下将数据插入到临时表中,然后使用 SELECT 执行 INSERT 到目标表中,同时为 INSERT 和 SELECT 启用并行化来完成所有这些艰苦的工作在服务器端完成?顺便说一句,“轮询线程没有保存,因为我想提高性能。”......我确信这分成 2 个连续的步骤不会提高性能,但会使情况变得更糟。在一个线程中使用批处理池并构建 PreparedStatement。尝试 2...N 个这样的线程同时运行...
  • 您也可以尝试同时加载到每个分区。假设您有 N 个分区,并且有 N 个线程池化并执行批处理。每个线程都在自己的特定分区中进行插入。

标签: java oracle optimization concurrency publish-subscribe


【解决方案1】:

通过批处理命令减少逐行处理的开销。许多 API 包含批处理命令的方法,或者您可以自己将它们与如下语句组合:

INSERT INTO products (product_no, name, price)
SELECT 1, 'Cheese', 9.99 FROM dual UNION ALL
SELECT 2, 'Bread' , 1.99 FROM dual UNION ALL
SELECT 3, 'Milk'  , 2.99 FROM dual;

【讨论】:

  • 是的,我在批处理语句中使用 PreparedStatement - addBatch 和 executeBatch。
  • 有一点你不能走得更远,你有数百万的数据,所以你可能需要数百万的查询。您可以优化,但总会有一个顶部。
猜你喜欢
  • 2014-10-17
  • 1970-01-01
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多