周期性数据库批插入的Java并发性
场景:一个线程每秒被调用数千次,以对同一个表执行插入操作,并且当前正在逐个执行这些操作 目标:改为定期批量插入以提高性能 当线程的周期性数据库批插入的Java并发性,java,database,multithreading,Java,Database,Multithreading,场景:一个线程每秒被调用数千次,以对同一个表执行插入操作,并且当前正在逐个执行这些操作 目标:改为定期批量插入以提高性能 当线程的saveItem方法被调用时,尝试使用TimerTask将保存的对象添加到列表中,然后每隔2秒左右将它们组合起来进行批插入 第一个想法是有两个列表,分别称为toSave和toSaveBackup。当调用线程的saveItem方法来保存某些内容时,它将被添加到toSave列表中,但一旦TimerTask启动并需要将所有内容保存到数据库中,它会将AtomicBoolean
saveItem
方法被调用时,尝试使用TimerTask将保存的对象添加到列表中,然后每隔2秒左右将它们组合起来进行批插入
第一个想法是有两个列表,分别称为toSave
和toSaveBackup
。当调用线程的saveItem
方法来保存某些内容时,它将被添加到toSave列表中,但一旦TimerTask启动并需要将所有内容保存到数据库中,它会将AtomicBoolean标志saveInProgress
设置为true。此标志由saveItem
检查,如果saveInProgress
为真,则它将添加到toSaveBackup而不是toSave。批量保存完成后,toSaveBackup中的所有项目都将移动到toSave列表中,列表上可能有一个同步块
这是否合理的做法?还是有更好的最佳实践?我的谷歌搜索技能让我失望,所以欢迎任何帮助
杂项信息:
- 所有这些插入都是在同一个表中
- 插入是通过接收MQTT消息来驱动的,所以在此之前,我不能将它们组合成一批
保存队列的大小以及批次保存后的时间。如果这些值中的任何一个超过了配置的限制(每2秒保存一次或每1000条记录保存一次),则我们将保存。LinkedBlockingQueue用于简化同步
再次感谢大家的帮助 看起来您的主要目标是等待预定义的时间量,然后触发插入。当插入正在进行时,您不希望其他插入请求等待插入完成。插入完成后,您希望对下一个插入请求再次重复相同的过程
基于上述理解,我将提出以下解决方案。你不需要有两个单独的清单来实现你的目标。还请注意,为了便于解释,我提出了一个老式的解决方案。在我的解释结束时,我将介绍一些您可以使用的其他API。下面是:
定义每N秒运行一次的计时器
和计时器任务
李>
定义一个ArrayList
,用于排队发送到saveItem
方法的插入请求李>
saveItem
方法可以围绕该ArrayList
定义一个syncronized
块。当调用saveItem
时,您可以在此synchronized
块中将项目添加到ArrayList
李>
在等式的另一边,TimerTask
应该在同一ArrayList
上以及在其run
方法中有一个synchronized
块。它应该在给定时刻将ArrayList
中存在的所有记录插入数据库。插入完成后,TimerTask
应清除ArrayList
并最终从synchronized
块中出来
您不再需要显式监视插入是否正在进行,或者在插入时创建ArrayList
的副本。在这种情况下,您的ArrayList
将成为共享资源
如果您还希望size
成为继续插入的决定因素,可以执行以下操作:
在TimerTask
中定义一个名为waittruents
的int。此字段表示如果列表的大小不够大,则TimerTask
不应执行任何操作的连续唤醒次数
每次TimerTask
唤醒时,它都可以执行类似于if(waitAttempts%3==0 | | list.size>10){插入数据}或者{递增waitAttempts,什么也不做。退出同步块和run方法}
。您可以将3
和10
更改为任何适合吞吐量要求的数字李>
注意内在锁定被用作解释该方法的一种手段。人们总是可以采用这种方法,并使用现代构造(如阻塞队列
)来实现它,这样就不需要在阵列列表上手动进行同步
。我还建议使用Executors.newSingleThreadScheduledExecutor()
而不是TimerTask
,因为它可以确保在任何给定时间只有一个线程运行,并且不会有线程重叠。另外,waitAttempts
的逻辑是指示性的,需要进行调整才能正常工作。为什么不将东西放入阻塞队列
,设置一个截止日期,并从“批插入”线程继续调用take()
(有一个超时),直到达到该截止日期?好吧,这是任何BlockingQueue
实现的经典用例。还有,为什么TimerTask
。为什么不使用预定义的批大小来决定何时插入批?我的意思是poll()
,而不是take()
take()
没有超时重载。我想在这里输入大小。正如@CKing提到的。db性能的影响来自size@CKing,仅在检查大小时,我们不会插入,除非我们有足够的,对吗?那个么,若生产商在达到这个规模之前就停止生产会怎么样呢?时间检查是必要的回答很好,但不完全是我想要的决定因素。而不是“只有当你有X个元素时才写,每Y秒检查一次”我说