0
点赞
收藏
分享

微信扫一扫

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

陆佃 2022-04-25 阅读 54
golangredis

说在前面

场景

  • 在使用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,详见

  • 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 条评论