在redis中存储(唯一)键值的数组

时间:2016-01-12 23:52:56

标签: ruby-on-rails postgresql redis

在某些活动中,应用中的用户会获得奖励/惩罚点数。

我想为每位用户存储:

1- Bonused [EventID,#points]

列表

2-惩罚列表[EventID,#points]

功能

  • 必须能够将新[EventID, #points]对推送到列表中。

  • 可以轻松检索EventID列表。

  • #points可以是十进制,而不仅仅是整数。

E.g。

用户#10

  • 奖金:[[122,10],[128,10],[133,5]]

  • 刑罚:[[22,10],[28,5],[13,1]]

·用户#1 余额:(10 + 10 + 5) - (10 + 5 + 1)= 9

目前为止的选项

我正在使用postgres + Redis。

1。 Postgres的HSTORE

E.g。 User.find(10).bonus_events => {'122':'10','128':'10','133':'5'}

2。 Postgres的JSON类型

E.g。 User.find(10).bonus_events => [{id:'122',points:'10'}, {id:'128',points:'10'} {id:'133',points:'5'}]

3。 Redis?

不相信......

1 个答案:

答案 0 :(得分:0)

ZSET案例

ZSET(排序集)可以这种方式使用。在你的问题中,eventId和points是整数,所以Redis会以最大可能的效率存储它们。

来自datatypes intro

  

Redis排序集是非重复的字符串集合。不同之处在于,排序集的每个成员都与得分相关联,用于从最小得分到最高得分排序。虽然成员是独一无二的,但可能会重复分数。

     

使用有序集,您可以以非常快的方式添加,删除或更新元素(在与元素数量的对数成比例的时间内)。由于元素按顺序排列而不是之后排序,因此您还可以通过分数或排名(位置)以非常快的方式获取范围。访问排序集的中间也非常快,因此您可以使用排序集作为非重复元素的智能列表,您可以快速访问所需的所有内容:按顺序排列元素,快速存在测试,快速访问中间元素!

解决方案:

  1. 每位用户使用2个ZSET - 一个用于奖励,一个用于惩罚。
  2. EventID是关键,#points是ZSET的得分。
  3. 添加活动:

    ZADD bonuses:1 10 122 
    ZADD penalization:1 10 22
    ...
    

    聚集他们(伪代码):

    function getBalance(userId) {
        bonusKey = "bonuses:" + userId;
        penalizationKey = "penalization:" + userIdl
    
        bonuses = redis.call("ZRANGE", bonusKey, 0 -1, "WITHSCORES")
        penalization = redis.call("ZRANGE", penalizationKey, 0 -1, "WITHSCORES")
    
        //`bonuses` and `penalization` are array of eventId->points,
        // so use index 1 to aceess just points
        return bonuses.reduse(acc, element => acc + element[1]) + 
               penalization.reduse(acc, element => acc + element[1]);
    }
    

    HSET案例

      

    仅当您在每个列表中都有唯一的eventId时才使用此案例

    Redis Hashes是字符串字段和字符串值之间的映射,因此它们是表示对象的完美数据类型(例如,具有多个字段的用户,如姓名,姓氏,年龄等)。具有少量字段的哈希以一种占用空间非常小的方式存储,因此您可以在一个小的Redis实例中存储数百万个对象。

    解决方案:

    1. 每个用户使用2个HSET - 一个用于奖励,一个用于惩罚(使用可能认为存储两个列表,其中一个带有前缀,例如奖励:eventId - 不要这样做。这不允许redis到{{3 }})。
    2. EventID是关键,#points是HSET的值。
    3. 添加活动:

      HSET bonuses:1 122 10
      HSET penalization:1 22 10
      ...
      

      聚集他们(伪代码):

      function getBalance(userId) {
          bonusKey = "bonuses:" + userId;
          penalizationKey = "penalization:" + userIdl
      
          bonuses = redis.call("HGETALL", bonusKey)
          penalization = redis.call("HGETALL", penalizationKey)
      
          //`bonuses` and `penalization` are array of eventId->points,
          // so use index 1 to aceess just points
          return bonuses.reduse(acc, element => acc + element[1]) + 
                 penalization.reduse(acc, element => acc + element[1]);
      }
      

      什么更好

      HSET案例使用RAM越来越好。如果每个列表中只有唯一的eventId - 请使用这种情况。