【golang/redis】redis中大数字自动转换成指数形式的处理

说在前面

  • go version:go1.14.1 windows/amd64
  • redis version:5.0.14

场景

  • 在使用redis的有序集合(sorted set)实现排行榜功能的时候,通常会对成员(member)的分数(score)进行一定的设计;例如最简单的分数榜,可以使用:
    u i n t 64 ( s c o r e ) < < 32 ∣ u i n t 64 ( 0 × F F F F F F F F − u i n t 32 ( c u r _ t i m e _ s t a m p ) ) uint64(score) << 32 | uint64(0\times FFFFFFFF-uint32(cur\_time\_stamp)) uint64(score)<<32uint64(0×FFFFFFFFuint32(cur_time_stamp))
    作为成员的分数(即前32位使用实际的分数,后32位使用最大uint32值减去当前的unix时间戳),这样,在相同分数下,先达成的成员将排在前面。

  • 这样的设计理论上其实是没有问题的;但是有序集合中的score的数据类型其实是double详见

    Redis sorted sets use a double 64-bit floating point number to represent the score.
    In all the architectures we support, this is represented as an IEEE 754 floating point number, that is able to represent precisely integer numbers between -(2^53) and +(2^53) included. In more practical terms, all the integers between -9007199254740992 and 9007199254740992 are perfectly representable.
    Larger integers, or fractions, are internally represented in exponential form, so it is possible that you get only an approximation of the decimal number, or of the very big integer, that you set as score.

  • score超过一定大小后,就会转为指数形式;例如

    127.0.0.1:6379> zadd test_key 9007199254740991 a
    127.0.0.1:6379> zrange test_key 0 -1 WITHSCORES
    1) "a"
    2) "9007199254740991"
    // 这个时候还是很正常的
    127.0.0.1:6379> zadd test_key 9007199254740993 a
    127.0.0.1:6379> zrange test_key 0 -1 WITHSCORES
    1) "a"
    2) "9007199254740992"
    127.0.0.1:6379> zadd test_key 10007199254740993 a
    127.0.0.1:6379> zrange test_key 0 -1 WITHSCORES
    1) "a"
    2) "10007199254740992"
    // 只是+1,已经出现精度丢失了
    127.0.0.1:6379> zadd test_key 110007199254740993 a
    127.0.0.1:6379> zrange test_key 0 -1 WITHSCORES
    1) "a"
    2) "1.1000719925474099e+17"
    // 使用指数形式表示了
    
  • 在转成指数形式时候,如果还是将从redis中取出来的数据转成uint64,那么就会转换不过去。

  • 这个问题在score相对比较小的时候,不太会出现;但是如果score设计的时候更加细分,比如前32位使用16位的等级+16位的经验,那么就会很容易出现。

解决方式

  • score设计上避免出现问题

    • 例如将后面的时间戳数据换一种形式记录,比如分钟、小时、天,而不使用秒,从而降低bit位的占用,将更多的bit位放在score上
  • 其他

    • 暂时没有想到什么更好的方法;这本质上是uint64float64的问题,当数值大了之后,一定会有精度差异;
    • 即使使用类型安全的redis库,例如go-redis,也会面临这个问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值