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

时间:2017-02-07 14:31:05

标签: java database multithreading

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

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

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

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

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

其他信息:

  • 所有这些插页都在同一张表中
  • 插入是通过接收MQTT消息来驱动的,因此我不能在此之前批量合并它们

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

再次感谢大家的帮助!

1 个答案:

答案 0 :(得分:2)

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

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

  1. 定义将每N秒运行一次的TimerTimerTask
  2. 定义ArrayList,用于排队发送到saveItem方法的插入请求。
  3. saveItem方法可以围绕此sycnrhonized定义ArrayList块。您可以在调用ArrayList时向synchronized块中的saveItem添加项目。
  4. 在等式的另一方面,TimerTasksynchronized方法的同一ArrayList内应该有一个run块。它应该将当前ArrayList中存在的所有记录插入到数据库中。插入完成后,TimerTask应该clear ArrayList并最终退出synchronized块。
  5. 当插入正在进行时,您将不再需要显式监视插入是否正在进行或创建ArrayList的副本。在这种情况下,您的ArrayList将成为共享资源。

    如果您还希望size成为继续插入的决定性因素,您可以这样做:

    1. waitAttempts中定义名为TimerTask的int。此字段指示TimerTask如果size的{​​{1}}不够大,则list不应执行任何操作的连续唤醒次数。
    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 }之类的操作。您可以将310更改为适合您的吞吐量要求的任何数字。
    3. 注意使用内在锁定作为解释方法的手段。人们总是可以采用这种方法并使用现代结构(例如BlockingQueue)来实现它,从而无需在synchronize上手动ArrayList。我还建议使用Executors.newSingleThreadScheduledExecutor()而不是TimerTask,因为它确保在任何给定时间只有一个线程运行,并且线程不会重叠。此外,waitAttempts的逻辑是指示性的,需要进行调整才能正常工作。