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\""
......