【问题标题】:Spring Data CrudRepository multithreads causing "save unique constraint" conflictSpring Data CrudRepository 多线程导致“保存唯一约束”冲突
【发布时间】:2017-05-18 12:52:15
【问题描述】:

我有一种情况,两个线程正在尝试检查 userId,当它没有出现时,它们会尝试创建它。问题是,userId 是 SQL 创建的,当第二个线程尝试保存它时,它已经通过了“if user == null”错误检查。

User.java 的相关部分:

@Column(name = "username", unique=true, length = 150)
private String username;

DemoApp.java:

            String usrName = "testuser";
            // ur is a CrudRepository from Spring Boot for Users
            User existingU = ur.findOneByUsername(usrName);  // Both Threads cannot find usrName 
            if(existingU == null){ 
                //#thread 1 is able to save the user, but thread 2 causes "unique constraint SQL error." 
                existingU = new User(usrName, "firstname", "lastname");
                ur.save(existingU); // Thread 1 succeeds here... Thread 2 fail
                lg.info("Saved new user"); // Thread 1 outputs this.
                // Thread 2 errors out on .save and crashes
            } else {
                lg.info("User found in database"); // this never happens
            }

UserRepository(用于你的变量)

import org.springframework.data.repository.CrudRepository;


public interface UserRepository extends CrudRepository<User, Long> {
    User findOneByUsername(String userName);
}

问题:如何确保 CrudRepository findByUsername 是最新的并且不会进入现有U==null if 语句?有没有更优雅的方式来设计这种情况?也许没有解决方案,因为线程似乎在同一毫秒启动。

【问题讨论】:

    标签: multithreading spring-data spring-data-jpa


    【解决方案1】:

    为了防止这种情况发生,您必须确保一次只有一个线程执行您的代码。

    我看到了两种方法:

    1. 把它放在一个同步块中,在同一个对象上同步。这只有在您的事务完全包含在同步块中并且您的所有代码都在一台机器上运行时才有效。后者在 2017 年似乎不合理,即使是“Hello World”也有望扩展到至少六台机器。所以……

    2. 使数据库执行锁定。您可以从数据库中选择数据,这样在您提交事务之前没有人可以访问数据。这称为SELECT FOR UPDATE。在您没有要更新的行的情况下,您想创建一行。当您选择带有SELECT FOR UPDATE 的完整表(或者实际上是具有唯一约束的列)时,您应该能够做到这一点。

    不要这样做! 所描述的方法将在完整索引上创建一个排他锁 => 没有插入(这就是你的意思),没有更新,没有删除,并且根据 RDBMS,可能会进一步限制什么样的操作会被阻止。这可能会严重限制可扩展性。

    因此,在沿着这条路线走之前,请重新考虑您是否不能将您的操作放在一个块中,并在失败的情况下重试。容易做到;没有数据库依赖;没有 SQL 魔法,并且假设您实际上很少遇到冲突,可能会获得更好的性能和可伸缩性。

    如果您真的想这样做,上面的链接应该可以帮助您入门。您将该代码放入custom method implementation for your repository

    【讨论】:

    • 这个解决方案让我觉得我应该解决两个请求永远不应该同时进入的问题,因为无论如何都不会有两个用户拥有相同的用户名,所以我只需要弄清楚为什么有“两个线程”,如我的另一个问题中所述 :: stackoverflow.com/questions/44033738/… 不确定我是否想在 Spring Boot 应用程序中执行同步块。
    猜你喜欢
    • 2017-04-15
    • 1970-01-01
    • 2015-04-22
    • 1970-01-01
    • 1970-01-01
    • 2021-05-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多