更新一堆行是基于事务还是基于行?

Updating a bunch of rows is it transaction based or row based?

我有一个 table 有一个需要不断重新计算的列,我希望这个 table 是可缩放的。用户也必须能够在上面书写。

没有服务器和并发用户很难测试这类东西,至少我不知道如何。 那么这两个选项中的一个可行吗?

@ApplicationScoped
public class Abean {
   @EJB
   private MyService myService;
   @Asynchronous
   public void computeTheData(){
      long i = 1;
      long numberOfRows = myService.getCountRows(); // gives the number of row in the table
      while(i<numberOfRows){
        myService.updateMyRow(i);
      }
      computeTheData(); // recursion so it never stops, I'm wondering if this wouldn't spawn more threads and if it would be an issue.
   }
}

public class MyService implements MyServiceInterface{
    ...
    public void updateMyRows(int row){
       Query query = em.createQuery("SELECT m FROM MyEntity WHERE m.id=:id");
       Query.setParameter("id", row);
       List<MyEntity> myEntities = (MyEntity) query.getResultList();
       myEntity.computeData();
    }
}

VS

@ApplicationScoped
public class Abean {
   @EJB
   private MyService myService;
   @Asynchronous
   public void computeTheData(){
      myService.updateAllRows();
   }
}

public class MyService implements MyServiceInterface{
        ...
    public void updateAllRows(int page){
       Query query = em.createQuery("SELECT m FROM MyEntity");
       List<MyEntity> myEntities = (MyEntity) query.getResultList();
       myEntity.computeData();
    }
}

这些可行吗?我正在使用 mysql,table 的引擎是 innoDB。

您应该在更新前使用悲观锁定来锁定修改的行,这样用户的手动修改就不会与后台更新发生冲突。如果您不使用锁定,您的用户的修改有时会回滚,如果它们与修改同一行的后台作业发生冲突。

此外,对于悲观锁定,如果您的用户的事务等待获取锁的时间超过发生超时的时间,您的用户可能会遇到回滚。为防止这种情况,您应该使所有使用悲观锁的事务尽可能短。因此,后台作业应该为每一行或每一小组行创建一个新事务,如果它可能 运行 比合理时间长。只有在事务完成后才会释放锁(用户将等待直到锁被释放)。

您的 MyService 的示例,运行在单独的事务中更新每个更新(实际上,您可以 运行 在单个事务中批量更新多个,传递列表或 ID 范围作为 updateMyRows 的参数):

public class MyService implements MyServiceInterface{
        ...
    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) // this will create a new transaction when running this method from another bean, e.g. from Abean
    public void updateMyRows(int row){
       TypedQuery<MyEntity> query = em.createQuery(SELECT m FROM MyEntity WHERE m.id=:id", MyEntity.class);
       query.setParameter("id", row);
       query.setLockMode(LockModeType.PESSIMISTIC_WRITE); // this will lock all entities retrieved by the query
       List<MyEntity> myEntities = query.getResultList();
       if (!myEntities.isEmpty()) {
         myEntities.get(0).computeData();
       }
    }
}

当where条件只使用id时,可以考虑em.find(row, MyEntity.class, LockModeType.PESSIMISTIC_WRITE).computeData()而不是使用查询(在em.find()后添加空指针检查)

其他说明:

从问题中不清楚您是如何触发后台作业的。 运行 正如您在示例中所写的那样,无限的工作一方面不会创建额外的线程(当您在同一个 bean 上调用方法时,注释不会被递归地考虑)。另一方面,如果出现异常,你的后台作业至少应该处理异常,这样它才不会被停止。您可能还想在后续执行之间添加一些等待时间。

最好运行后台作业作为计划作业。一种可能的选择是 @Schedule 注释而不是 @Asynchronous。您可以指定作业在后台执行的频率。然后检查你的工作的开始,之前的执行是否完成是很好的。 Java EE 7 的另一个选项是使用 ManagedScheduledExecutorService 以指定的时间间隔定期触发后台作业。