代码之家  ›  专栏  ›  技术社区  ›  user3170736

Java-用于预订的分布式JPA锁

  •  0
  • user3170736  · 技术社区  · 6 年前

    我正在开发一个遗留系统,它允许预订安排。该应用程序是无状态REST,设计为水平扩展。然而,数据库是在所有实例之间共享的。在我得到一个关于设计和规模的讲座之前,它不是我的——我必须充分利用糟糕的情况(或代码库)。最近,我们看到了一个存在重复预订的问题。我相信这是因为请求-响应线程的性质。当前的过程是,接收请求,检查数据库是否存在冲突的时间预约,如果没有,则插入。根据读取和插入之间的时间,两者都可能被插入。场景看起来是这样的:

    |------|-------|-------|
    R1     C1      I1     RSP
    
    -|--------|-------|---------|
    R2       C2     I2   RSP
    

    其中R=请求,C=数据库检查,I=插入。

    所以我相信我可以使用@Synchronized注释,它将强制所有线程被排序。问题在于,有多个实例正在运行,因此无法在所有实例中运行。悲观或乐观的读写似乎不适用,因为我们正在尝试读写组合,除非我完全误解了。有什么想法可以全面解决这个问题吗?更愿意通过表锁或类似的方式在java中处理它,而不是添加额外的服务(kafka、redis等)。

    编辑: 数据库看起来像这样,在开发中使用h2,在生产中使用mysql。

     id |  start_time  | locationid | postingid | userid | durration
     --------------------------------------------------------------- 
    
    0 回复  |  直到 6 年前
        1
  •  5
  •   dpr    6 年前

    从零开始实现这样的东西不是火箭科学,但也许你想看看这个GitHub项目: https://github.com/alturkovic/distributed-lock

    我既没有参与这个项目,也没有使用它,但它看起来很有前途。您只需要使用创建一个Spring配置 EnableJdbcDistributedLock :

    @Configuration
    @EnableJdbcDistributedLock
    public class LockConfiguration {
    }
    

    并创建所需的数据库表:

    create table lock (
      id int not null auto_increment primary key,
      lock_key varchar(255) unique,
      token varchar(255),
      expireAt timestamp,
    );
    

    一旦这一切就绪,您就可以通过一个简单的注释(取自项目示例)在分布式环境中同步方法调用:

    @JdbcLocked(expression = "#name")
    public String sayHello(final String name) {
      return "Hello " + name + "!";
    }
    
        2
  •  1
  •   user3099140 user3099140    6 年前

    数据库应该在处理这个问题。你可以:

    您可以使用命令行SQL客户端在本地测试您的更改:打开两个并发事务,并尝试以不同的方式交错这些事务 SELECT 还有 INSERT .

        3
  •  0
  •   ewramner    6 年前

    我认为合理的做法是锁定位置。如果我对域名的理解正确,您是在为开始时间和持续时间给定的时间段预订特定位置(如房间)?在这种情况下,您可能已经有了一个位置实体。如果没有,你也许可以创建一个并添加一个匹配的表。这样代码就简单了:

    1. 使用JPA从请求中获取位置的悲观锁
    2. 检查数据库中是否存在冲突
    3. 插入新的预订
    4. 犯罪

    表锁确实会损害可伸缩性,但锁定特定位置应该可以。