为什么Redis key会自动删除?

问题描述 投票:0回答:1

我正在使用 Spring Data Redis 模块中的 CrudRepository 接口来访问 Redis 中的数据。 我的Redis连接配置如下: 这是我的代码:

  redis:
    database: 1
    host: my.redis.com
    port: 6380
    password: redis
    timeout: 5000ms 
    lettuce:
      pool:
        max-active: 50 
        max-idle: 10 

我的Redis配置类如下:

@Configuration
@Slf4j
@EnableCaching
public class RedisConfig {
    @Bean
    public RedisTemplate<String, byte[]> byteRedisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<String, byte[]> template = new RedisTemplate<>();
        template.setConnectionFactory(connectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(RedisSerializer.byteArray());
        return template;
    }
    @Bean
    public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
    {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        //设置value hashValue值的序列化
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(
                Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
        serializer.setObjectMapper(om);
        redisTemplate.setValueSerializer(serializer);
        redisTemplate.setHashValueSerializer(serializer);
        //key hasKey的序列化
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .disableCachingNullValues()
                .entryTtl(Duration.ofMinutes(10))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        RedisCacheManager redisCacheManager = RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(redisCacheConfiguration)
                .build();

        return redisCacheManager;
    }

}

以下是我需要保存在Redis中的数据类:

@Data
@AllArgsConstructor
@NoArgsConstructor
@RedisHash(RedisKeyPrefix.UNIT_CONNECT_STATUS)
@Builder
public class UnitConnectStatus implements Serializable {

    @Id
    private Integer unitId;
    private Integer unitType;
    private Boolean server2gateway;
    private Boolean gateway2unit;
}

我的repo类写成如下。正如你所看到的,它只是扩展了一个 CrudRepository,这非常简单。这就是我使用这种方法与 Redis 交互的原因:

public interface ConnectRepo extends CrudRepository<UnitConnectStatus,Integer> {
}

在我的整个项目中,我只有像saveOnline这样的几个方法来保存设备的在线状态,如下所示。值得注意的是,我使用@Async注解将数据异步存储在Redis中。正如你所看到的,我没有为 Redis 值设置过期时间:

 @Async
 public <T extends Unit>void saveOnline(T unit){
        Assert.notNull(unit, "unit is null");
        Assert.notNull(unit.getId(), "unit id is null");
        connectRepo.save(UnitConnectStatus.builder()
                .unitId(unit.getId())
                .unitType(unit.getType())
                .server2gateway(true)
                .gateway2unit(true)
                .build());
 }

我可以确认我的项目中没有代码执行删除键“RedisKeyPrefix.UNIT_CONNECT_STATUS”的操作。现在,奇怪的事情正在发生。我注意到,在项目运行时,某些 unitConnectStatus 对象偶尔会被读取为 null。我使用 id=57 的 unitConnectStatus 作为示例创建了一个简单的再现测试方法:

 @Test
 public void testCon(){
        for (int i = 0; i < 10000; i++) {
            log.info("{}",connectRepo.findById(56).orElse(null));
            Thread.sleep(200);
        }
 }
2024-01-26 11:39:48.361 [][] INFO  c.t.controller.TestLeila:20  - UnitConnectStatus(unitId=56, unitType=1, server2gateway=true, gateway2unit=true)
2024-01-26 11:39:48.582 [][] INFO  c.t.controller.TestLeila:20  - UnitConnectStatus(unitId=56, unitType=1, server2gateway=true, gateway2unit=true)
2024-01-26 11:39:48.803 [][] INFO  c.t.controller.TestLeila:20  - UnitConnectStatus(unitId=56, unitType=1, server2gateway=true, gateway2unit=true)
2024-01-26 11:39:49.023 [][] INFO  c.t.controller.TestLeila:20  - null
2024-01-26 11:39:49.235 [][] INFO  c.t.controller.TestLeila:20  - UnitConnectStatus(unitId=56, unitType=1, server2gateway=true, gateway2unit=true)
2024-01-26 11:39:49.453 [][] INFO  c.t.controller.TestLeila:20  - UnitConnectStatus(unitId=56, unitType=1, server2gateway=true, gateway2unit=true)
2024-01-26 11:39:49.673 [][] INFO  c.t.controller.TestLeila:20  - UnitConnectStatus(unitId=56, unitType=1, server2gateway=true, gateway2unit=true)
2024-01-26 11:39:49.906 [][] INFO  c.t.controller.TestLeila:20  - UnitConnectStatus(unitId=56, unitType=1, server2gateway=true, gateway2unit=true)

从一系列输出中可以看到,findById 得到的结果偶尔会为 null。理论上,我的密钥没有过期、失效或删除的逻辑。为什么偶尔查询会返回null?

我认为我的测试方法中不应该查询空值

java spring-boot redis spring-data-redis
1个回答
0
投票

问题是SpringDataRedis的CrudRepository的save方法在操作RedisHash数据时不是原子性的,所以如果频繁调用findBy()方法和save()方法,可能会读取到null。

© www.soinside.com 2019 - 2024. All rights reserved.