定期数据库批量插入的Java并发

场景:每秒调用一个线程数千次,以便对同一个表进行插入,并且当前正在逐个执行这些操作。

目标:定期进行批量插入以提高性能。

当线程的saveItem方法被调用时,尝试使用TimerTask来将保存的对象添加到列表中,然后每隔2秒左右将它们组合成批量插入。

首先想到的是有两个列表,称之为toSavetoSaveBackup 。 当调用线程的saveItem方法来保存它时,它将被添加到toSave列表中,但是一旦TimerTask启动并需要将所有内容保存到数据库,它就会将AtomicBoolean标志saveInProgress设置为true。 saveItem检查此标志,如果saveInProgress为true,它将添加到toSaveBackup而不是toSave。 批量保存完成后,toSaveBackup中的所有项目都将移动到toSave列表,可能是列表上的同步块。

这是一种合理的方法吗? 还是有更好的最佳做法? 我的谷歌搜索技能让我失望,所以欢迎任何帮助。

其他信息:

  • 所有这些插入都在同一个表中
  • 插入是通过接收MQTT消息来驱动的,因此在此之前我无法将它们组合在一起

更新:对CKing以下答案的调整达到了预期的方法:TimerTask每100毫秒运行一次并检查saveQueue的大小以及自批量保存以来的时间。 如果这些值中的任何一个超过配置的限制(每2秒或每1000条记录保存等),我们就会保存。 LinkedBlockingQueue用于简化同步。

再次感谢大家的帮助!

看起来您的主要目标是等待预定义的时间,然后触发插入。 当插入正在进行时,您不必插入其他插入请求,直到插入完成。 插入完成后,您希望再次为下一个插入请求重复相同的过程。

考虑到上述理解,我会提出以下解决方案。 您无需拥有两个单独的列表即可实现目标。 另请注意,为了解释,我提出了一个老式的解决方案。 我将介绍一些您在解释结束时可以使用的其他API。 开始 :

  1. 定义一个每N秒运行一次的TimerTimerTask
  2. 定义一个ArrayList ,用于排队发送到saveItem方法的插入请求。
  3. saveItem方法可以在此ArrayList周围定义一个sycnrhonized块。 您可以在调用saveItem时将项添加到此synchronized块中的ArrayList
  4. 在等式的另一方面, TimerTask应该在其run方法中的同一ArrayList上具有synchronized块。 它应该将给定时刻ArrayList中存在的所有记录插入到数据库中。 插入完成后, TimerTaskclear ArrayList并最终退出synchronized块。

您将不再需要显式监视插入是否正在进行中,或者在插入正在进行时创建ArrayList的副本。 在这种情况下,您的ArrayList将成为共享资源。

如果您还希望size成为继续插入的决定性因素,则可以执行以下操作:

  1. TimerTask定义一个名为waitAttempts的int。 此字段指示如果listsize不够大, TimerTask应该不执行任何操作的连续唤醒次数。
  2. 每次TimerTask唤醒时,它都可以执行if(waitAttempts%3==0 || list.size > 10) { insert data } else { increment waitAttempts and do nothing. Exit the synchronized block and the run method } if(waitAttempts%3==0 || list.size > 10) { insert data } else { increment waitAttempts and do nothing. Exit the synchronized block and the run method } 。 您可以将310更改为适合您的吞吐量要求的任何数字。

注意使用内在锁定作为解释方法的手段。 人们总是可以采用这种方法并使用现代结构(如BlockingQueue来实现它,这将消除在ArrayList上手动synchronize的需要。 我还建议使用Executors.newSingleThreadScheduledExecutor()而不是TimerTask因为它确保在任何给定时间只有一个线程运行,并且线程不会重叠。 此外, waitAttempts的逻辑是指示性的,需要调整才能正常工作。