Redis Springboot hash value ERR hash value is not an integer

本文介绍了如何在SpringBoot项目中配置Redis缓存,使用FastJson进行序列化,并解决FastJson到Redis键值序列化时的问题。

一、问题背景

在 Redis 中,如果对某个 哈希字段 使用了 HINCRBY / HINCRBYFLOAT 等需要数值运算的命令,那么该字段的值必须是 可被 Redis 解析为整数或浮点数的字符串,否则会抛出以下错误:

ERR hash value is not an integer

例如,如果 Redis 中某哈希字段存储的值是 "20abc" 或者序列化后带有 双引号 的字符串(如 "\"20\""),那么它无法被正确识别为数字。


二、为什么会出现此问题

1. 传统序列化器(JSON、JDK 等)常见行为

  • 当使用 JSON 序列化一个 Number(如 Integer, Long),有些序列化器可能会将其转换为字符串并加上 双引号(例如 "20"),导致 Redis 解析时认定它是一个字符串,而不是可做算术运算的“数字字符串”。
  • 也有一些序列化器会直接输出 数字(例如 20),这种方式 Redis 就能识别并用于 HINCRBY 操作。

2. FastJson2 默认行为

  • FastJson2 在序列化 Number 时,通常会以 裸值 的形式输出,例如 Integer(20) 会序列化成字节数组对应的 20(不带引号),这样 Redis 能将其当作数字识别。
  • 但是,如果在项目中做过其他特殊设置(例如包装了一层字符串)或在反序列化过程中将数字又转成字符串,就可能导致 Redis 里存储的数据不是纯数字。

三、代码示例

自定义 FastRedisSerializer 继承自 GenericFastJsonRedisSerializer,并重写了 serialize 方法:

class FastRedisSerializer extends GenericFastJsonRedisSerializer {
    @Override
    public byte[] serialize(Object object) throws SerializationException {
        if (object == null) {
            return new byte[0];
        }
        //处理hash value 存储 无法执行自增  ERR hash value is not an integer
        if (object instanceof Number) {
            return JSON.toJSONBytes(object);
        }
        return super.serialize(object);
    }
}
  1. 判空:如果 object == null,返回空字节数组。
  2. 数值类型处理:如果 objectNumber 的子类 (Integer, Long, Double 等),则使用 JSON.toJSONBytes(object) 来序列化。
    • 在 FastJson2 中,这通常会得到一个不带引号的数字字节数组,例如 20 -> [50, 48] (ASCII 的 ‘2’ ‘0’)。
    • 这样存储到 Redis 后,值就是 20,Redis 可以直接做自增操作。
  3. 其他情况:使用父类 GenericFastJsonRedisSerializer 的逻辑来序列化。

关键点:当我们明确知道某个字段值需要在 Redis 端进行数值运算(如自增、自减),就应该将其序列化成 裸数字(无引号),这样 Redis 能识别它为可增量运算的数值字符串。


四、如何验证序列化结果是否符合预期

  1. 在代码里直接测试

    Number num = 20;
    byte[] bytes = JSON.toJSONBytes(num);
    System.out.println(new String(bytes)); 
    // 预期输出: 20 (不带引号)
    
  2. 在 Redis CLI 或 Redis GUI 中查看实际存储值
    你可以在 Spring Boot 启动后,往 Redis 写入一个哈希值并执行 HGET 查看:

    HGET your_hash your_field
    

    如果返回的就是 20(而不是 "20"),说明序列化结果是纯数字。

  3. 尝试自增操作
    在 Redis CLI 中,尝试执行:

    HINCRBY your_hash your_field 1
    

    如果能成功并返回新的数值,说明 Redis 认可它为整数字符串。


五、需要注意的其他事项

  1. 反序列化
    当你使用 FastRedisSerializer 存入纯数字时,在读取(反序列化)时也要能正确将其识别为 Number 类型。

    • GenericFastJsonRedisSerializer 通常能自动识别数字并转成对应的 IntegerLong
    • 如果需要更严格的类型控制,可以自定义 TypeReference 或者在应用层保证类型一致。
  2. Hash 中混合类型
    如果同一个 Hash 中有些字段需要自增运算,有些字段是字符串等其他类型,最好在业务逻辑中加以区分或在存储层合理命名,避免序列化/反序列化时出现类型冲突。

  3. 注意日志或其他监控
    如果不慎将纯数字字段改成字符串,或者在后续版本中更改了序列化方式,就可能再次遇到这个问题。建议在业务层或测试环节增加校验,确保要做自增运算的值在 Redis 中确实是纯数字格式。

  4. 其它替代做法
    如果你仅仅是针对某几个字段需要执行自增操作,而其余字段不需要,那么在写入 Redis 时,可以只对需要自增的字段 直接使用 redisTemplate.opsForHash().increment(...),这样就不走序列化的流程,会让 Redis 原生处理数值运算,避免了类型转换带来的麻烦。


六、总结

  • 核心问题:自增操作对存储在 Redis Hash 中的值有格式要求,必须是可解析为数字的字符串。
  • 解决思路:在序列化数值类型时,确保序列化结果是 裸数字,而不是带引号的字符串。
  • 你的方案:继承 GenericFastJsonRedisSerializer,对 Number 类型单独处理,JSON.toJSONBytes() 生成的通常是无引号的数字字节数组,可被 Redis 识别为整数。

通过以上方式,就能解决 ERR hash value is not an integer 的问题。如果在使用过程中依然出现此错误,建议再从以下几个方面排查:

  1. 是否真的进入了 FastRedisSerializerserialize 分支(打印日志或断点调试)。
  2. JSON.toJSONBytes(object) 的输出是否可能被别的逻辑再封装成字符串。
  3. Redis 是否已经存在了带引号或非数字格式的旧值。

只要确保最终写入 Redis 的是纯数字,就可以使用 Redis 原生的 HINCRBYHINCRBYFLOAT 等命令进行自增操作。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十方来财

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值