问题描叙
出现这种乱码前缀的原因是没有进行序列化,因此导致在传输过程出现乱码问题,存到数据库,发现 key,hash key/value 都有 \xAC\xED\x00\x05t\x00 前缀。RedisTemplate类中默认是没有设置序列化的。
解决方法
设置RedisTemplate的序列化方式
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
public class RedisConfig {
@Bean(name = "redisTemplate")
//配置redisTemplate
//默认情况下redisTemplate只能支持redisTemplate<String,String>
//需要自定义 RedisTemplate,设置序列化
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory factory){
RedisTemplate<String,Object> template = new RedisTemplate <>();
template.setConnectionFactory(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用jackson
template.setHashValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
注意:用了StringRedisSerializer之后,redis库的数据都是以String类型保存,所以int类型的整数,在保存时,会类型上升, 先变成double,然后再存成String,也就是1会存成"1.0",所以如果保存int数据时,需要自己在代码中单独处理下。上面修改全局配置不能兼容新老数据,新增配置可以兼容。
修改序列化方式后,数据不兼容报错
org.springframework.data.redis.serializer.SerializationException: Cannot deserialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to deserialize payload. Is the byte array a result of corresponding serialization for DefaultDeserializer?; nested exception is java.io.StreamCorruptedException: invalid stream header: E5A487E6
使用多个redisTemplate配置
@Configuration
public class RedisConfig {
@Bean
@Primary
public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new StringRedisSerializer());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
return redisTemplate;
}
@Bean(name = "ByteRedisTemplate")
public RedisTemplate ByteRedisTemplate(
RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(RedisSerializer.byteArray());
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(RedisSerializer.byteArray());
return redisTemplate;
}
}
使用方法
@RestController
public class PublishDemo {
@Autowired
private RedisTemplate redisTemplate;
@Autowired
@Qualifier("ByteRedisTemplate")
private RedisTemplate byteRedisTemplate;
@RequestMapping("set")
public void set() {
String key1 = "t1";
String value = "12345";
String key2 = "t2";
String value2 = "54321";
redisTemplate.opsForValue().set(key1, value);
byteRedisTemplate.opsForValue().set(key2, value2.getBytes());
}
@RequestMapping("get")
public void get() {
Object t1 = redisTemplate.opsForValue().get("t1");
System.out.println("t1:" + t1.toString());
Object o = byteRedisTemplate.opsForValue().get("t2");
System.out.println("t2:" + Arrays.toString((byte[]) o));
}
}
对象存hash
entityMap.forEach((field, value) -> redisTemplate.opsForHash().put(String.format(REDIS_CONN_KEY, entity.getId()), field, value != null ? String.valueOf(value) : ""));
redis判断连接状态,重连机制
if (!isset($this->redis) || !$this->redis->isConnected()) {
$this->setRedis();
}
$this->redis->exists($key);
/**
* 连接redis.
*/
private function setRedis()
{
if (isset($this->redis) && $this->redis->isConnected()) {
$this->redis->disconnect();
}
$this->redis = new RedisClient($this->container->getParameter('redis_mq')); //重连redis;
}
Redis工具类
/**
* Redis工具类,提供了一系列操作Redis缓存的方法。
*/
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
public class RedisUtil {
/**
* Redis模板对象,用于执行Redis操作。
*/
private RedisTemplate<String, Object> redisTemplate;
/**
* 设置Redis模板对象。
*
* @param redisTemplate Redis模板对象。
*/
public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
//=============================common============================
/**
* 为给定的键设置缓存失效时间。
*
* @param key 键
* @param time 失效时间(秒)
* @return 设置是否成功
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 获取给定键的缓存失效时间。
*
* @param key 键
* @return 失效时间(秒),如果为0表示永不过期
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 检查给定键是否存在。
*
* @param key 键
* @return 如果存在返回true,否则返回false
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除一个或多个键的缓存。
*
* @param key 键,可以是多个
*/
@SuppressWarnings("unchecked")
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
//============================String=============================
/**
* 从缓存中获取字符串键的值。
*
* @param key 键
* @return 值,如果键不存在则返回null
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 保存数字到Redis中,以字符串形式存储。
*
* @param key Redis中键的名称。
* @param value 要保存的数字值。
*/
public void saveNumber(String key, int value) {
redisTemplate.opsForValue().set(key, String.valueOf(value));
}
/**
* 从Redis中获取保存的数字。
*
* @param key Redis中键的名称。
* @return 如果键存在,则返回对应的数字值;如果键不存在,返回0。
*/
public int getNumber(String key) {
int number = 0;
Object value = get(key);
if (value != null) {
number = Integer.parseInt((String) value);
}
return number;
}
/**
* 将值存储到缓存中。
*
* @param key 键
* @param value 值
* @return 存储是否成功
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将值存储到缓存中,并设置过期时间。
*
* @param key 键
* @param value 值
* @param time 过期时间(秒)time要大于0 如果time小于等于0 将设置无限期
* @return 存储是否成功
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 尝试在Redis中设置给定的键值对,如果键不存在。redis的过期机制和分布式锁
* <p>
* 此方法尝试为给定的键设置一个值,只有在键当前不存在的情况下才成功。
*
* @param key 要设置的键,不能为null。
* @param value 要设置的值,不能为null。
* @param time 设置值的过期时间,单位为秒。
* @return 如果设置成功(或键已存在),则返回true;如果发生异常,则返回false。
*/
public boolean setIfAbsent(String key, Object value, long time) {
try {
// 检查键和值是否为null,如果是,则抛出运行时异常。
if (key == null || value == null) {
throw new RuntimeException("key/value不能为null");
}
// 如果键不存在,则设置键的值,并指定过期时间。
return redisTemplate.opsForValue().setIfAbsent(key, value, time, TimeUnit.SECONDS);
} catch (Exception e) {
// 捕获任何异常,打印堆栈跟踪,并返回false。
e.printStackTrace();
return false;
}
}
/**
* 对给定键的值进行递增操作。
*
* @param key 键
* @param delta 递增值,必须大于0
* @return 增加后的值
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 对给定键的值进行递减操作。
*
* @param key 键
* @param delta 递减值,必须大于0
* @return 减少后的值
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
//================================Map=================================
/**
* 从Hash中获取指定字段的值。
*
* @param key 键
* @param item 字段
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取Hash中所有字段和值。
*
* @param key 键
* @return 字段-值的映射
*/
public Map<Object, Object> hmget(String key) {
return redisTemplate.opsForHash().entries(key);
}
/**
* 向Hash中设置多个字段和值。
*
* @param key 键
* @param map 字段-值的映射
* @return 设置是否成功
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向Hash中设置多个字段和值,并设置过期时间。
*
* @param key 键
* @param map 字段-值的映射
* @param time 过期时间(秒)
* @return 设置是否成功
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向Hash中设置字段和值。
*
* @param key 键
* @param item 字段
* @param value 值
* @return 设置是否成功
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向指定的hash中添加数据,如果key不存在则创建。
*
* @param key Redis中的键。
* @param item Hash中的字段。
* @param value Hash中字段对应的值。
* @param time 过期时间,单位为秒。如果大于0,则设置过期时间;否则,不设置过期时间。
* @return 如果操作成功返回true,否则返回false。
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 从hash中删除指定的字段。
*
* @param key Redis中的键。
* @param item 要删除的字段,可以是多个。
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 检查hash中是否存在指定字段。
*
* @param key Redis中的键。
* @param item 要检查的字段。
* @return 如果字段存在返回true,否则返回false。
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* 为hash中的指定字段增加指定数值。
*
* @param key Redis中的键。
* @param item Hash中的字段。
* @param by 要增加的数值。
* @return 增加后的值。
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* 为hash中的指定字段减少指定数值。
*
* @param key Redis中的键。
* @param item Hash中的字段。
* @param by 要减少的数值。
* @return 减少后的值。
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
//============================set=============================
/**
* 获取set中的所有成员。
*
* @param key Redis中的键。
* @return set中的所有成员。
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 检查set中是否包含指定成员。
*
* @param key Redis中的键。
* @param value 要检查的成员。
* @return 如果set包含指定成员返回true,否则返回false。
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向set中添加成员。
*
* @param key Redis中的键。
* @param values 要添加的成员,可以是多个。
* @return 成功添加的成员数。
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 向set中添加成员并设置过期时间。
*
* @param key Redis中的键。
* @param time 过期时间,单位为秒。
* @param values 要添加的成员,可以是多个。
* @return 成功添加的成员数。
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0) {
expire(key, time);
}
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取Set集合的大小。
*
* @param key Set集合的键。
* @return Set集合的大小。
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 从Set集合中移除指定的值。
*
* @param key Set集合的键。
* @param values 要移除的值,可以是多个。
* @return 被移除的元素数量。
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
//===============================list=================================
/**
* 获取List集合中指定范围的元素。
*
* @param key List集合的键。
* @param start 起始索引。
* @param end 结束索引 0 到 -1代表所有值。
* @return 指定范围内的元素列表。
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取List集合的大小。
*
* @param key List集合的键。
* @return List集合的大小。
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取List集合中指定索引的元素。
*
* @param key List集合的键。
* @param index 索引,index>=0时,0表示列表第一个元素,依次类推;index<0时,-1表示列表最后一个元素,依次类推。
* @return 指定索引的元素。
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 在List集合的尾部添加一个元素。
*
* @param key List集合的键。
* @param value 要添加的元素。
* @return 是否添加成功。
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 在List集合的尾部添加一个元素,并设置过期时间。
*
* @param key List集合的键。
* @param value 要添加的元素。
* @param time 过期时间,单位为秒。
* @return 是否添加成功。
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将给定的列表值添加到指定键的列表末尾。如果列表不存在,会创建一个空列表。
*
* @param key 键
* @param value 列表值
* @return 如果操作成功返回true,否则返回false。
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将给定的列表值添加到指定键的列表末尾,并设置过期时间。如果列表不存在,会创建一个空列表。
*
* @param key 键
* @param value 列表值
* @param time 过期时间,单位为秒
* @return 如果操作成功返回true,否则返回false。
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 更新列表中指定索引位置的元素值。
*
* @param key 键
* @param index 索引位置
* @param value 新的元素值
* @return 如果操作成功返回true,否则返回false。
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 从列表中移除指定数量与给定值相等的元素。
*
* @param key 键
* @param count 要移除的元素数量,可以是正数(从头部开始移除)或负数(从尾部开始移除)
* @param value 要移除的元素值
* @return 实际移除的元素数量。
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 添加一个元素, zset与set最大的区别就是每个元素都有一个score,因此有个排序的辅助功能; zadd
*
* @param key
* @param value
* @param score
*/
public boolean zsetAdd(String key, Object value, Double score) {
try {
redisTemplate.opsForZSet().add(key, value, score);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除元素 zrem
*
* @param key
* @param value
*/
public void remove(String key, String value) {
redisTemplate.opsForZSet().remove(key, value);
}
/**
* 删除zSet 中过期元素
*
* @param key
* @param v1
* @param v2
*/
public void removeRangeByScore(String key, Double v1, Double v2) {
redisTemplate.opsForZSet().removeRangeByScore(key, v1, v2);
}
/**
* @param key
* @param value
* @param score
*/
public Double incrScore(String key, String value, double score) {
return redisTemplate.opsForZSet().incrementScore(key, value, score);
}
/**
* 查询value对应的score zscore
*
* @param key
* @param value
* @return
*/
public Double score(String key, String value) {
return redisTemplate.opsForZSet().score(key, value);
}
/**
* 判断value在zset中的排名 zrank
*
* @param key
* @param value
* @return
*/
public Long rank(String key, String value) {
return redisTemplate.opsForZSet().rank(key, value);
}
/**
* 返回集合的长度
*
* @param key
* @return
*/
public Long size(String key) {
return redisTemplate.opsForZSet().zCard(key);
}
/**
* 查询集合中指定顺序的值, 0 -1 表示获取全部的集合内容 zrange
* <p>
* 返回有序的集合,score小的在前面
*
* @param key
* @param start
* @param end
* @return
*/
public Set<Object> range(String key, int start, int end) {
return redisTemplate.opsForZSet().range(key, start, end);
}
/**
* 获取字符串类型
*
* @param key
* @return
*/
public String getString(String key) {
Object value = redisTemplate.opsForValue().get(key);
if (null != value) {
return key == null ? null : String.valueOf(value);
} else {
return null;
}
}
}