Redis Hash序列化存储的问题及解决方案

目录
  • SDR序列化方式有多种
  • 对Redis的存储设置是我自己写的
  • 更改序列化方法
  • 更改序列化方式
  • 继续使用JdkSerializationRedisSerializer

这里说的是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,而且这种效率是最高的,没办法,毕竟是原生的。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • Redis 哈希Hash底层数据结构详解

    目录 1. Redis 底层数据结构 2. hashtable 3. redisDb 与 redisObject 4. ziplist 5. linkedlist 6. quicklist 1. Redis 底层数据结构 Redis数据库就像是一个哈希表,首先对key进行哈希运算得到哈希值再取模得到一个下标,每个元素是一个节点,节点之间形成链表.这感觉有点像Java中的HashMap. 不同的数据类型的实现方式是不一样的,可以通过object encoding命令查看底层真正的数据存储结构 同一

  • redis如何取hash的值

    目录 redis取hash的值 redis中存值是Hash冲突如何解决 总结 redis取hash的值 命令:可以取得对应的hash中的具体值 hmget s_account:208 account accountname 结果:“www” 如图 命令:取得hash中的所有key hkeys s_account:208 结果: redis中存值是Hash冲突如何解决 Redis 中的 Hash和 Java的 HashMap 更加相似,都是数组+链表的结构.当发生 hash 碰撞时将会把元素追加到

  • Redis数据类型string和Hash详解

    目录 String类型命令操作 设置指定key的值 获取指定key的值 返回key中字符串值的子串 获取多个给定key的值 返回key所对应的字符串的长度 设置一个或多个键值对 将key中所存储的数值加一 将key中所存储的数值减一 字符串追加 Hash类型 设置一个Hash数据 获取指定哈希表中所有的字段和值 获取存储在哈希表中指定字段的值 删除一个或多个哈希表字段 获取哈希表中字段的数量 获取哈希表中的所有字段 获取哈希表中所有的值 摘要:Redis中有五大数据类型,分别是String.Li

  • Redis基本数据类型哈希Hash常用操作

    目录 Redis数据类型Hash常用操作 一.hset 二.hget 三.hmset 四.hmget 五.hgetall 六.hdel 七.hlen 八.hexists 九.hkeys 十.hvals 十一.hincrby 十二.hsetnx Redis数据类型Hash常用操作 redis里的hash是一个string类型的field(字段)和value(值)的映射表.特别适合用于存储对象,每个hash可以存储40多亿键值对. 熟悉python的童鞋可以想象成字典dict.之前的数据类型存储都是

  • Redis 存储对象信息用 Hash 和String的区别

    目录 前言 String Hash String 和 Hash 占用内存的比较 网友讨论 总结 前言 Redis 内部使用一个 RedisObject 对象来表示所有的 key 和 value,RedisObject 中的 type,则是代表一个 value 对象具体是何种数据类型,它包含字符串(String).链表(List).哈希结构(Hash).集合(Set).有序集合(Sorted set). 日常工作中我们存储对象信息的时候,一般有两种做法,一种是用 Hash 存储,另一种是 Stri

  • RedisTemplate常用操作方法总结(set、hash、list、string等)

    目录 String类型 Hash类型 List类型 Set类型 zSet类型 Redis常用的数据类型: String Hash List Set zSet Sorted set String类型 保存和读取String(最常用的) System.out.println("缓存正在设置........."); redisTemplate.opsForValue().set("key1","value1"); redisTemplate.opsFo

  • Redis Hash序列化存储的问题及解决方案

    目录 SDR序列化方式有多种 对Redis的存储设置是我自己写的 更改序列化方法 更改序列化方式 继续使用JdkSerializationRedisSerializer 这里说的是Spring Data Redis(一下简称SDR)设置Hash存储的序列化. SDR序列化方式有多种 如: StringRedisSerializer JdkSerializationRedisSerializer Jackson2JsonRedisSerializer OxmSerializer 等等 目前我有个需

  • SpringBoot整合Redis实现序列化存储Java对象的操作方法

    目录 一.背景 1.思考 2.方案 二.源码分析 三.注入RedisTemplate 1.引入依赖 2.Redis 连接信息 3.Redis 核心配置类 4.Redis工具类 四.测试 1.创建 Java 实体类 UserInfo 2.测试用例 3.测试结果 之前介绍过 https://www.jb51.net/article/223539.htm 我们可以看出,在 SpringBoot 对 Redis 做了一系列的自动装配,使用还是非常方便的 一.背景 1.思考 通过我们前面的学习,我们已经可

  • Redis序列化存储及日期格式的问题处理

    目录 Redis序列化存储及日期格式 可视化界面看到保存的数据是这样的 这时候就需要我们自定义序列化方式 Redis序列化LocalDateTime报错 方案一:实体类日期字段添加注解 方案二:设置Redis对日期序列化处理 Redis序列化存储及日期格式 在模块开发中,使用Redis做缓存是非常常见的技术,当我们注入RedisTempate模板时 redisTemplate.opsForValue().set("item_"+id,itemModel,10, TimeUnit.MIN

  • python redis存入字典序列化存储教程

    在python中通过redis hset存储字典时,必须主动把字典通过json.dumps()序列化为字符串后再存储, 不然hget获取后将无法通过json.loads()反序列化为字典 序列化存储 r = redis_conn() r.hset('wait_task', 'one', json.dumps({'project': 'india', 'total_size': '15.8 MB'})) r.hset('wait_task', 'two', json.dumps({'project

  • 浅谈python处理json和redis hash的坑

    1.使用MySQLdb读取出来的数据是unicode字符串,如果要写入redis的hash中会变成 "{u'eth0_outFlow': 2.5, u'eth1_inFlow': 3.44}" 无法使用json.loads,需要提前将unicode转成str: str(eth0_outFlow) 2.单引号包围的key不是规范的json格式 "{'eth0_outFlow': 2.5, 'eth1_inFlow': 3.44}" 需要转成规范的格式才能使用json.

  • Redis存取序列化与反序列化性能问题详解

    1. 问题场景 我们在使用Redis的时候经常会将对象序列化存储到Redis中,在取出的时候进行反序列化,如果对象过大在进行序列化和反序列化的时候会有一定性能问题.今天查看了CSRedis源码发现在Set和Get的时候是支持Byte[]类型,那么问题来了如果我们将对象转换成Byte[]类型进行存储是否会比序列化和反序列化操作快了? 2. 问题验证 2.1. 编写一个简单实例进行验证 List<User> list = new List<User>(); for (int i = 0

  • 解析redis hash应用场景和常用命令

    存储对象类数据 hmset user name huyongjian age 23 height 170 常用命令 将多个 field-value对设置到哈希表 key 中 hmset user name huyongjian age 23 height 170 将哈希表 key 中的字段 field 的值设为 value hset user weight 70 只有在字段 field 不存在时,设置哈希表字段的值 hsetnx user address china 获取存储在哈希表中指定字段的

  • 浅谈Redis跟MySQL的双写问题解决方案

    目录 写在前面 三种读写缓存策略 Cache-AsidePattern(旁路缓存模式) Read-Through/Write-Through(读写穿透) WriteBehindPattern(异步缓存写入) 旁路缓存模式解析 CacheAsidePattern的一些疑问 CacheAsidePattern的缺陷 项目中有遇到这个问题,跟MySQL中的数据不一致,研究一番发现这里面细节并不简单,特此记录一下. 写在前面 严格意义上任何非原子操作都不可能保证一致性,除非用阻塞读写实现强一致性,所以缓

  • SpringBoot结合Redis实现序列化的方法详解

    目录 前言 配置类 配置 Jackson2JsonRedisSerializer 序列化策略 配置  RedisTemplate 配置缓存策略 测试代码 完整代码 前言 最近在学习Spring Boot结合Redis时看了一些网上的教程,发现这些教程要么比较老,要么不知道从哪抄得,运行起来有问题.这里分享一下我最新学到的写法 默认情况下,Spring 为我们提供了一个 RedisTemplate 来进行对 Redis 的操作,但是 RedisTemplate 默认配置的是使用Java本机序列化.

  • PHP使用Redis替代文件存储Session的方法

    本文实例讲述了PHP使用Redis替代文件存储Session的方法.分享给大家供大家参考,具体如下: PHP默认使用文件存储session,如果并发量大,效率非常低.而Redis对高并发的支持非常好,所以,可以使用redis替代文件存储session. 这里,介绍下php的session_set_save_handler 函数的作用和使用方法.该函数定义用户级session保存函数(如打开.关闭.写入等). 原型如下: bool session_set_save_hanler(callback

随机推荐