Spring Data Redis并发问题

Héc*_*nga 7 java concurrency spring multithreading spring-data-redis

在将多个线程与 spring-redis-data 一起使用时,我遇到了一个大问题,而且它很容易重现,以至于我认为我错过了一些微不足道的东西。

开门见山

如果我在执行保存操作时查询 CrudRepository,有时(高达 60%)在 Redis 上找不到记录。

环境

代码

尽管可以在上面的链接中找到完整的代码,但以下是主要组件:

Crud存储库

@Repository
public interface MyEntityRepository extends CrudRepository<MyEntity, Integer> {

}
Run Code Online (Sandbox Code Playgroud)

实体

@RedisHash("my-entity")
public class MyEntity implements Serializable {

    @Id
    private int id1;

    private double attribute1;
    private String attribute2;
    private String attribute3;
Run Code Online (Sandbox Code Playgroud)

控制器

    @GetMapping( "/my-endpoint")
    public ResponseEntity<?> myEndpoint () {

        MyEntity myEntity = new MyEntity();
        myEntity.setAttribute1(0.7);
        myEntity.setAttribute2("attr2");
        myEntity.setAttribute3("attr3");
        myEntity.setId1(1);

        myEntityRepository.save(myEntity);//create it in redis

        logger.info("STARTED");

        for (int i = 0; i < 100; i++){
            new Thread(){
                @Override
                public void run() {
                    super.run();

                    myEntity.setAttribute1(Math.random());

                    myEntityRepository.save(myEntity); //updating the entity

                    Optional<MyEntity> optionalMyEntity = myEntityRepository.findById(1);
                    if (optionalMyEntity.isPresent()) {
                        logger.info("found");
                    }else{
                        logger.warning("NOT FOUND");
                    }
                }
            }.start();

        }

        return ResponseEntity.noContent().build();
    }

Run Code Online (Sandbox Code Playgroud)

结果

2020-05-26 07:52:53.769  INFO 30655 --- [nio-8080-exec-2] my-controller-logger                     : STARTED
2020-05-26 07:52:53.795  INFO 30655 --- [     Thread-168] my-controller-logger                     : found
2020-05-26 07:52:53.798  WARN 30655 --- [     Thread-174] my-controller-logger                     : NOT FOUND
2020-05-26 07:52:53.798  WARN 30655 --- [     Thread-173] my-controller-logger                     : NOT FOUND
2020-05-26 07:52:53.806  INFO 30655 --- [     Thread-170] my-controller-logger                     : found
2020-05-26 07:52:53.806  WARN 30655 --- [     Thread-172] my-controller-logger                     : NOT FOUND
2020-05-26 07:52:53.812  WARN 30655 --- [     Thread-175] my-controller-logger                     : NOT FOUND
2020-05-26 07:52:53.814  WARN 30655 --- [     Thread-176] my-controller-logger                     : NOT FOUND
2020-05-26 07:52:53.819  WARN 30655 --- [     Thread-169] my-controller-logger                     : NOT FOUND
2020-05-26 07:52:53.826  INFO 30655 --- [     Thread-171] my-controller-logger                     : found
2020-05-26 07:52:53.829  INFO 30655 --- [     Thread-177] my-controller-logger                     : found
Run Code Online (Sandbox Code Playgroud)

因此,只需 10 个线程,其中 6 个就无法在数据库中找到结果。

替换为spring data redis

正如这里提到的,用 Spring Data Redis 替换 Redis 至少包含 9 个操作。

第一个结论

因此,为了替换redis中的值,它必须删除哈希值、索引,然后再次添加新哈希值和新索引,也许一个线程正在执行此操作,而其他线程试图查找该值按索引,该索引尚未添加。

第二个结论

我认为带有data-redis的spring data几乎不可能有这样的bug,所以我想知道我对data-redis或redis的不理解。由于 redis 具有并发性,我认为可能会发生不同的情况,但根据提供的示例,似乎是这样......

预先感谢大家

sam*_*cde 2

这张票也提出了同样的问题。

该行为是有意选择的,以避免散列条目滞留。删除哈希可以确保一致的状态,并避免出现不应再属于哈希的其他条目。
Redis 存储库操作不是原子的。

所以它不是原子的
并在票证中建议,解决方案将使用PartialUpdate.

下面是一个例子的片段

    @Autowired
    private RedisKeyValueTemplate redisKVTemplate;
    ...
    // id is the @Id value of the entity
    private void update(Integer id) {
        PartialUpdate update = new PartialUpdate<MyEntity>(id, MyEntity.class)
                .set("attribute1", Math.random());
        redisKVTemplate.update(update);
    }
Run Code Online (Sandbox Code Playgroud)

参考资料:
使用 spring-data-redis 更新 redis 中的实体