Java 多工作线程和数据库同步

Java 多工作线程和数据库同步,java,postgresql,transactions,Java,Postgresql,Transactions,我有多个线程将文件保存在磁盘上,并将这些信息放入数据库 在应用程序的另一端,我有多个线程从DB读取此信息,并按文件id一次处理一个提到的文件: 选择*从文件到按文件id处理订单 我发明的是制作一个处理状态列,它有四种状态新建,处理,失败,成功 每个工作人员都应该只从按ID排序的数据库中读取状态为“新建”的一行,并立即更新为“处理”状态,因此其他工作人员不会处理同一文件 但是,有些事情告诉我,我可能会以一些比赛条件结束 交易能解决这个问题吗 不幸的是,我不能在事务内部进行所有操作,因为处理文件需要

我有多个线程将文件保存在磁盘上,并将这些信息放入数据库

在应用程序的另一端,我有多个线程从DB读取此信息,并按
文件id
一次处理一个提到的文件:

选择*从文件到按文件id处理订单

我发明的是制作一个
处理状态
列,它有四种状态
新建
处理
失败
成功

每个工作人员都应该只从按ID排序的数据库中读取状态为“新建”的一行,并立即更新为“处理”状态,因此其他工作人员不会处理同一文件

但是,有些事情告诉我,我可能会以一些比赛条件结束

交易能解决这个问题吗

不幸的是,我不能在事务内部进行所有操作,因为处理文件需要很多时间,事务池将耗尽,所以我必须按照以下顺序进行两个事务

  • [在事务中]提取行并更新到状态
    处理
  • [无事务]处理文件
  • [事务中]根据结果将最终状态更新为
    成功
    失败

  • 请尝试一个简单的互斥示例:

    try {
      mutex.acquire();
      try {
        // access and update record to processing
      } finally {
        mutex.release();
      }
    } catch(InterruptedException ie) {
      // ...
    }
    
    根据您的代码,您可以通过各种方式锁定它,请参阅:

    编辑:


    对不起,这是一个C++的测试用例,这是java版本

    相当恼人的,更新在PostgreSQL中没有限制。 您可以这样做:

    update files_to_process set processing_status='PROCESSING' where file_id = (
        SELECT file_id FROM files_to_process 
          WHERE processing_status = 'NEW' 
          ORDER BY file_id FOR UPDATE SKIP LOCKED LIMIT 1
    ) returning *;
    
    有了这个公式,就不应该有种族条件。您可以单独在事务中运行它(或者在autocommit下,只需运行该语句,它就会自动形成自己的事务)

    但我可能会把它改成“机器加工”,而不仅仅是“加工”,或者类似的东西。否则,如果以不干净的方式失败,您如何知道处理何时失败?(这是在一个事务中完成的一件好事,失败应该自行回滚)

    不幸的是,我不能在事务内部进行所有操作,因为处理文件需要很多时间,事务池将耗尽

    但是,您永远不应该拥有比可用CPU更多的未完成事务。除非您有一个非常大的计算场,否则您应该能够使池足够大。但这种方法的一个大问题是,你无法了解正在发生的事情

    对于双事务处理方法,为了提高性能,您可能需要创建一个部分索引:

    create index on files_to_process (file_id ) where processing_status = 'NEW';
    

    否则,您将不得不挖掘所有已完成且文件id较低的文件,以找到下一个新文件,最终这将变得缓慢。您可能还需要比默认设置更积极地清理桌子。

    也许我离此很远,但您可以在新的桌面上使用互斥,因为这听起来是一个非常快速的操作吗?@Pearl您能详细说明您的建议吗?我不确定我是否理解,但我对互斥有基本的了解。一些简单的例子?当一个线程需要访问数据库获取一条新记录,然后更新它来处理它时,在该代码部分使用互斥锁,这样一次只有一个线程这样做。只要一次只有一个应用程序在运行,这应该可以工作?我认为您的解决方案可以工作,但是我必须对它进行一些修改,因为我需要创建一个连接以从另一个表获取字段。你觉得这样可以吗?抱歉格式化<代码>更新file.thumbnails SET status='NEW'FROM(从file.files ff JOIN file.thumbnails ft ON ff.id=ft.file\u id中选择ft.id、ff.stored\u路径,其中ft.status='NEW'顺序按ff.id更新跳过锁定限制1)sub,其中file\u id=sub.id返回sub.*关于您的建议:
    但您的未完成事务数不应超过可用的CPU数。
    您建议,允许大约3到5个工作人员在事务中执行长时间运行的操作,并允许其他人员执行和服务来自前端的请求?3到5个工作人员是否存在问题取决于它们的运行时间(分钟?小时?天?),以及其他非排队工作是什么。如果它们造成问题,则问题在于吸尘,而不是连接池。如果只有3到5个连接池,我一开始就不会为它们使用连接池。要么根本不使用,要么对队列工作人员绕过它。