gpt4 book ai didi

spring-data-jpa - 带有 JPA 锁的 Spring 处理时间长

转载 作者:行者123 更新时间:2023-12-04 07:14:42 25 4
gpt4 key购买 nike

我有多个 quartz worker
每个工作人员选择一个 db 记录(打印机),然后对其进行一些工作(通过网络从打印机读取信息)。
完成每项工作最多可能需要 30 秒到 1 分钟。

回到 JDBC 时代,我会运行(伪代码)

     printer = "select from printer where status=? for update"
do the work, (takes 1 min)
update the printer record.

我的问题是这种带有 PESSIMISTIC_WRITE 的方法是可以的:
public interface PrinterRepo extends CrudRepository<Printer,String>
{
@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT r FROM Printers r where r.status = :status")
Printer findOneAndLock(@Param("status")String status);
}

然后 worker :
@Transactional
public void execute(JobExecutionContext jobExecutionContext) {
Printer p = printerRepo.findOneAndLock("status");
//do the work here (30 sec to 1 min)
printerRepo.save(p);
}

根据我的理解,锁将在用@Transactional 注释的函数结束时释放,是否正确?
我的问题是其他 worker 会怎样?
他们会在等待 findOneAndLock 时饿死吗?

谢谢

最佳答案

无论您将使用哪种类型和级别的锁,以及其他 worker 会发生什么,长期锁以及长期事务都不是一个好的解决方案。恕我直言,在您的情况下最好使用不同的方法而没有任何锁定,例如,一个额外的表来记录打印机“锁定”:

create table printer_locks (
printer_id bigint not null constraint printer_locks_pkey primary key,
created_at timestamp not null,
worker_id bigint not null constraint fk_printer_locks_worker_id references workers,

constraint fk_printer_locks_printer_id foreign key (printer_id) references printers(id)
);

当一个 worker 想用某台打印机开始工作时,它首先尝试将记录插入到这个表中。然后,如果成功,它将开始工作。作业完成后,工作人员会删除此记录。

因为 printer_id列是独一无二的 - 其他工作人员将无法同时开始使用同一台打印机工作。

执行:

@Entity
@Table(name = "workers")
public class Worker {

@Id
@GeneratedValue(...)
private Long id;
// other stuff...
}

@Entity
@Table(name = "printers")
public class Printer {

@Id
@GeneratedValue(...)
private Long id;
// other stuff...
}

@Entity
@Table(name = "printer_locks")
public class PrinterLock {

@Id
private Long id;

private Instant createdAt = Instant.now();

@OneToOne(fetch = FetchType.LAZY)
@MapsId
private Printer printer;

@ManyToOne(fetch = FetchType.LAZY)
private Worker worker;

public PrinterLock(Printer printer, Worker worker) {
this.printer = printer;
this.worker = worker;
}

// other stuff...
}

public void execute(...) {

Worker worker = ...;
Long printerId = ...;

printerRepo.findById(printerId)
.map(printer -> {
try {
printerLockRepo.save(new PrinterLock(printer, worker));
try {
// do the work here (30 sec to 1 min)
} finally {
printerLockRepo.deleteById(printerId);
}
} catch(Exception e) {
log.warn("Printer {} is busy", printerId);
}
})
.orElseThrow(() -> new PrinterNotFoundException(printerId));
}

请注意 execute方法甚至没有 @Transactional注解。

这种方法的另一个优点是列 createdAt这使您可以控制悬挂作业。

进一步阅读:
  • Row Level Locking in Mysql
  • Pessimistic Locking in JPA
  • Explicit Locking in PostgreSQL
  • Locking Reads in MySQL
  • The best way to map a @OneToOne relationship with JPA and Hibernate
  • 关于spring-data-jpa - 带有 JPA 锁的 Spring 处理时间长,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55854618/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com