Redis incrByFloat ERR increment would produce NaN or Infinity

背景

项目中用到了Redis作为中间件,使用incrBy来承担一部分的累加计算。

数据量很大,要累加的这个值也很大。

代码如下:

ShardedJedisPipeline pipeline = jedis.pipelined();
# value就是字符串类型的值
pipeline.incrByFloat(redisKey, Double.valueOf(value));

报错

运行过程中出现了以下的错:

ERR increment would produce NaN or Infinity

21/06/08 18:13:07 ERROR Main: output to redis error because: redis.clients.jedis.exceptions.JedisDataException: ERR increment would produce NaN or Infinity
21/05/28 18:13:07 ERROR Main: message is: 20210609000500000|.....|Infinity

过程

一看ERR increment would produce NaN or Infinity,以为是累加的结果值太大了。

排查了下数据,发现数据累加起来都没超过Long.MAX_VALUE

于是把double转为了long,累加方法改为了incrBy,运行了一段时间后,又报了新的错。

21/06/08 21:48:41 ERROR Main: output to redis error because: redis.clients.jedis.exceptions.JedisDataException: ERR increment or decrement would overflow

ERR increment or decrement would overflow仔细一看,发现这个错才是真正的累加超过范围了。


于是去官网看了下,发现incrBy仅限于64位有符号整数,也就是最大值不能超过2的64次(9223372036854775807)。

再去验证了下数据,发现这次的累加值也的确超过了9223372036854775807。


既然这个错是超出范围,那上个错肯定不是了…

又找了找,找到了这篇文章https://stackoverflow.com/questions/36861472/what-happens-when-int64-maxvalue-is-exceded-with-redis-incr

做了以下的测试,也出现了我上面的那个值超出范围的错

127.0.0.1:6379> set value 9223372036854775807 (2 power 63 -1)
OK
127.0.0.1:6379> incr value
(error) ERR increment or decrement would overflow
127.0.0.1:6379> 

于是我也做了incrbyfloat的测试:

127.0.0.1:6379> incrbyfloat mwf 1111111111111111111111111111111111111111111111111111111111111111

"1111111111111111111082384117134316001359187045209948125833199616"

发现incrbyfloat了这么大的值,都不会出现值溢出,所以我的问题肯定不是值溢出。


于是又仔细看了报错日志:

21/06/08 18:13:07 ERROR Main: output to redis error because: redis.clients.jedis.exceptions.JedisDataException: ERR increment would produce NaN or Infinity
21/05/28 18:13:07 ERROR Main: message is: 20210609000500000|.....|Infinity

看到了Infinity这个罪魁祸首。

做如下测试:

127.0.0.1:6379> incrbyfloat mwf Infinity
(error) ERR increment would produce NaN or Infinity

果然是它导致的!!!

再输入非法的字符串:

127.0.0.1:6379> incrbyfloat mwf asd
(error) ERR value is not a valid float

报的错是不一样的。说明Infinity是被当做了一个有效的浮点数。


那么问题又来了。

为什么Java中Double.valueOf解析Infinity不报错呢???

去Java中试了下,还真不报错,返回的就是Infinity

点进去Double类看了看,真的有Infinity-InfinityNaN三个值。看来还是太年轻了。。。

解决办法

incrByFloat的值不是InfinityNaN就好了。

在Java中判断下是否是Infinity或者NaN,然后对异常值处理。

protected double doubleValue(String input){
    if(input == null || input.trim().equals("")){
        return 0.0d;
    }else{
        double v = Double.parseDouble(input);
        return Double.isInfinite(v) || Double.isNaN(v) ? 0.0d : v;
    }
}

注意

  • incrByFloatincrBy能累加的范围大很多,推荐使用incrByFloat
  • incrByFloat在传入InfinityNaN时,会出现ERR increment would produce NaN or Infinity这个错
  • Java中Double.valuesOf传入Infinity-InfinityNan时,不会报错,能正常解析并返回
  • 可以调用Double.isInfinite来判断是否是Infinity,调用isNaN判断是否是NaN

参考

https://redis.io/commands/incrby

https://stackoverflow.com/questions/36861472/what-happens-when-int64-maxvalue-is-exceded-with-redis-incr

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值