Redis 常用配置实践

Redis 常用配置实践

这些是我在使用 redis 的过程中自己总结的一些实践,当然也参考了网上的一些文章和思想,用于一些日常的配置,主要解决以下问题:

1、序列化问题

这个应该在 java 领域用 redis 的人都知道,SpringBoot 在集成 redis 时,使用的 key value 序列化器为 jdk 序列化方式,会导致在使用 redis-cli 查看时,key 都是乱码,难以定位问题或获取值,不过也有可能是正常的,如果项目中只使用 RedisTemplate<String,String> 的话,原因是这种方式被单独重写了,序列化方式已经不再是 jdk 了

java 复制代码
    /**
     * RedisTemplate 序列化配置
     * 当 redisTemplate 没有被显示声明为 RedisTemplate<String, String> 时, key 和 value 会默认使用 jdk 的序列化方法
     * {@link org.springframework.data.redis.core.RedisTemplate#afterPropertiesSet()}
     * 此时如果使用 redis-cli 连接客户端查看,会发现 key 和 value 是乱码
     * <p>
     * RedisTemplate<String, String> 不会出现序列化乱码的原因是: 该类型被单独重写了
     * {@link org.springframework.data.redis.core.StringRedisTemplate}
     * <p>
     * 同时, value 不要为 Long 类型, Redis 为了节省内存会把没有超出 Integer 范围的 Long 数据转为 Integer 存储
     */
    @Bean
    public <T> RedisTemplate<String, T> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, T> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(RedisSerializer.string());
        redisTemplate.setValueSerializer(RedisSerializer.json());
        redisTemplate.setHashKeySerializer(RedisSerializer.string());
        redisTemplate.setHashValueSerializer(RedisSerializer.json());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

2、多项目共用 redis 集群

因为是 redis 集群,所以不再有单机 redis 16 个库的概念,一般情况下,可能公司只有几套 redis 环境,多个项目共用,导致项目测试完想清空数据再次测试的话,难以处理之前在 redis 中的旧数据,最容易想到的解决方式肯定就是每个项目的缓存数据用不同的前缀

那么最方便的当然是自定义序列化器,重写 key 的序列化和反序列化方式,使每次存和读取的时候都自动加上前缀

java 复制代码
@Configuration
public class RedisConfig {

    @Autowired
    private PrefixRedisKeySerializer redisKeySerializer;

    @Bean
    public <T> RedisTemplate<String, T> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, T> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(redisKeySerializer);
        redisTemplate.setValueSerializer(RedisSerializer.json());
        redisTemplate.setHashKeySerializer(redisKeySerializer);
        redisTemplate.setHashValueSerializer(RedisSerializer.json());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate template = new StringRedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(redisKeySerializer);
        return template;
    }

}
java 复制代码
@Component("redisKeySerializer")
public class PrefixRedisKeySerializer extends StringRedisSerializer {

    private static final String RedisKeyPrefix = "prefix_";

    @Override
    public byte[] serialize(String s) {
        if (s == null) {
            return new byte[0];
        }
        String realKey = RedisKeyPrefix + s;
        return realKey.getBytes(StandardCharsets.UTF_8);
    }

    @Override
    public String deserialize(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return null;
        }
        String s = new String(bytes, StandardCharsets.UTF_8);
        if (StringUtil.isNullOrEmpty(s)) {
            return s;
        }
        int index = s.indexOf(RedisKeyPrefix);
        if (index != -1) {
            return s.substring(RedisKeyPrefix.length());
        }
        return s;
    }

}

当然也可以使用 @Value 注解这种,把前缀放在配置文件里~

3、项目启动后在 redis 存放热点数据

有一些数据,我们知道它是热点数据,或者知道一定会用到,可能会希望在项目启动后就把它存入 redis 中,一种是说从某个表查询然后存入 redis,另一种就是明确知道其 key 和 value,希望启动时候将其存入 redis

java 复制代码
@Component
public class RedisHotKeyRunner implements ApplicationRunner {

    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private ParamDao paramDao;

    @Override
    public void run(ApplicationArguments args) throws Exception {

        IntStream.rangeClosed(1, 15).forEach(i -> {
            Param param = new Param();
            param.setId(i);
            param.setCode("key_" + i);
            param.setValue("value_" + i);
            param.setType("type_" + i);
            paramDao.save(param);
        });

        redisTemplate.opsForValue().set("jzsj", "2025-06-16");
        redisTemplate.opsForValue().set("username", "zhangsan");

        IntStream.rangeClosed(0, 14).forEach(i -> {
            redisTemplate.opsForSet().add("user_set_" + i, "set");
        });

        // 这里只是从数据库查询并测试插入,实际业务自行定义到底存什么类型的数据
        List<Param> all = paramDao.findAll();
        all.forEach(param -> {
            redisTemplate.opsForHash().put("param", param.getCode(), param.getValue());
        });

    }

}

需要注意的地方是,如果设置了统一前缀,那么 hash 操作的 field 也会加上这个前缀哦~

bash 复制代码
127.0.0.1:6379> hgetall esjdjg_param
 1) "prefix_key_1"
 2) "\"value_1\""
 3) "prefix_key_2"
 4) "\"value_2\""
 5) "prefix_key_3"
 6) "\"value_3\""
 7) "prefix_key_4"
 8) "\"value_4\""
 ......
相关推荐
float_六七22 分钟前
SQL六大核心类别全解析
数据库·sql·oracle
Code季风2 小时前
将 gRPC 服务注册到 Consul:从配置到服务发现的完整实践(上)
数据库·微服务·go·json·服务发现·consul
Boilermaker19922 小时前
【Java EE】SpringIoC
前端·数据库·spring
来自宇宙的曹先生2 小时前
用 Spring Boot + Redis 实现哔哩哔哩弹幕系统(上篇博客改进版)
spring boot·redis·后端
霸王龙的小胳膊2 小时前
泛微虚拟视图-数据虚拟化集成
数据库
灵犀学长3 小时前
解锁Spring Boot多项目共享Redis:优雅Key命名结构指南
数据库·redis
轩情吖3 小时前
Qt的信号与槽(二)
数据库·c++·qt·信号·connect·信号槽·
ZeroNews内网穿透3 小时前
服装零售企业跨区域运营难题破解方案
java·大数据·运维·服务器·数据库·tcp/ip·零售
可观测性用观测云3 小时前
达梦数据库监控观测最佳实践
数据库
都叫我大帅哥3 小时前
Redis哨兵完全指南:从救火队员到集群守护神
redis