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\""
 ......
相关推荐
wuxuanok32 分钟前
SQL理解——INNER JOIN
数据库·sql
GreatSQL33 分钟前
工具分享-通过开源工具 tuning-primer快速巡检MySQL5.7
数据库
天天讯通39 分钟前
机器人系统对接线索平台好处
大数据·数据库·人工智能·机器人·语音识别
运维小杨2 小时前
Redis主从复制搭建
数据库·redis·缓存
染落林间色2 小时前
达梦数据库权限体系详解:系统权限与对象权限
数据库·后端·sql
小王子10242 小时前
Django模型查询与性能调优:告别N+1问题
数据库·django·查询·n+1
昵称是6硬币3 小时前
MongoDB系列教程-第三章:PyMongo操作MongoDB数据库(1)—— 连接、基本CRUD操作
数据库·mongodb
HeyZoeHey3 小时前
MongoDB用户认证authSource
数据库·mongodb