周期性数据库批插入的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消息来驱动的,所以在此之前,我不能将它们组合成一批
更新:对CKK下面的答案进行了调整,达到了预期的效果:TimerTask每100毫秒运行一次,并检查
保存队列的大小以及批次保存后的时间。如果这些值中的任何一个超过了配置的限制(每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秒检查一次”我说