Redis Hash序列化存储的问题及解决方案
作者:月未明
这里说的是Spring Data Redis(一下简称SDR)设置Hash存储的序列化。
SDR序列化方式有多种
如:
StringRedisSerializer
JdkSerializationRedisSerializer
Jackson2JsonRedisSerializer
OxmSerializer
- 等等
目前我有个需求,是将数据用hash的形式存到Redis数据库中,在网上搜了下实现方式,部分代码如下:
@Bean public RedisTemplate<String,Object> redisTemplate(){ RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); initDomainRedisTemplate(redisTemplate, redisConnectionFactory); return redisTemplate; } /** * 设置数据存入 redis 的序列化方式 * * @param redisTemplate * @param factory */ private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) { redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setConnectionFactory(factory); } /** * 实例化 HashOperations 对象,可以使用 Hash 类型操作 * * @param redisTemplate * @return */ @Bean public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) { return redisTemplate.opsForHash(); }
对Redis的存储设置是我自己写的
/** * 添加 * * @param key key * @param filed filed * @param domain 对象 */ public void hset(String key,String filed,Object domain){ System.out.println("开始使用filed设置"); hashOperations.put(key, filed, domain); } /** * 查询 * * @param key 查询的key * @param field 查询的field * @return */ public Object hget(String key,String field) { return hashOperations.get(key, field); }
方法:
@RequestMapping("/mytest") public Object myTest() { redisUtils.hset("mykey","myfield","myvalue"); return redisUtils.hget("mykey","myfield"); }
Hash的存储跟String有些不同,从表面上看Hash多了个field,这个自己稍微想下就可以理解了。
执行上面的代码后,用客户端查看所存储的值:
上图显示的是乱码。
用redis-cli查看:
这里显示的是我存的值myvalue前多了些东西,这是序列化的时候所加的一些东西。
执行方法时前端得到的值:
这里可见从redis中取出的值是跟我存入的完全一样的(这是因为取出的时候Spring有做反序列化处理)。
如果从redis-cli中直接存储:
host:6379> hset mykey2 myfield2 myvalue2 (integer) 1 host:6379> hget mykey2 myfield2 "myvalue2"
查看客户端中的值:
在这里存入的hash显示的是正常的。
所以我猜测之前redis桌面客户端显示“不正常”的原因应该是出在序列化的时候。
更改序列化方法
改为StringRedisSerializer方式(一般key都是字符串,所以继续使用StringRedisSerializer,这里把Hash的value序列化改为StringRedisSerializer):
/** * 设置数据存入 redis 的序列化方式 * * @param redisTemplate * @param factory */ private void initDomainRedisTemplate(RedisTemplate<String, Object> redisTemplate, RedisConnectionFactory factory) { redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setHashKeySerializer(new StringRedisSerializer()); redisTemplate.setHashValueSerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); redisTemplate.setConnectionFactory(factory); }
查看客户端的值:
这时显示OK了,redis-cli中显示的也是OK的。
所以,我们遇到的问题貌似解决了。
因为我要存储的是hash,而hashOperations为我们提供了另外一个方法putAll,这个方法支持对HashMap的操作。
代码:
/** * 添加 * * @param key key * @param hm 要存入的hash表 */ public void hset(String key, HashMap<String,Object> hm){ System.out.println("开始使用hashmap设置"); hashOperations.putAll(key,hm); }
因为我的hashmap中要存的值包含时间,所以就要把值设为Object,代码:
@RequestMapping("/hm") public void hmsetTest() { HashMap<String,Object> hm =new HashMap<String,Object>(); hm.put("myFieldKey","myFieldKey"); hm.put("createTime",new Date()); redisUtils.hset("mykey",hm); }
执行结果:
这时在调用的时候直接报错了,说是Date类型无法转String。
回到单个值存入的方法上:
public void hset(String key,String filed,Object domain){ System.out.println("开始使用filed设置"); hashOperations.put(key, filed, domain); }
用这里执行Date的存储,结果还是包这个异常。
由此可见,使用StringRedisSerializer序列化并不能解决我们的问题,而且还有使用的限制。OxmSerializer这个东西我不太熟悉,所以没有测试。
使用Jackson2JsonRedisSerializer
更改序列化方式
redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<Object>(Object.class));
执行单个日期操作结果如下:
这里显示的日期被转成时间戳形式存储的。
执行hashmap:
结果显示执行的也是成功的,如果跟StringRedisSerializer比较会发现,存储字符串的时候值得最外层会被加上“”。
继续使用JdkSerializationRedisSerializer
可以正常存储,但是显示形式一样不是我们期望的。
对于这个问题我网上有种解决方法,在redis-cli中查看的时候使用–raw指令
即启动指令为:redis-cli –raw。这种方式也可以正常的查看中文。但是查看的时候日期依然有问题,而且字符串前边会多些东西(t)。
OxmSerializer这个东西我不熟悉,所以就没有测试,但是网上一般都说建议使用JdkSerializationRedisSerializer,而且这种效率是最高的,没办法,毕竟是原生的。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。