【问题标题】:How to "EXPIRE" the "HSET" child key in redis?如何在redis中“过期”“HSET”子键?
【发布时间】:2013-05-08 20:39:05
【问题描述】:

我需要使 redis 哈希中所有超过 1 个月的键过期。

【问题讨论】:

    标签: hash redis


    【解决方案1】:

    This is not possible,为了keeping Redis simple

    Quoth Antirez,Redis 的创建者:

    嗨,这是不可能的,要么为此使用不同的顶级密钥 特定字段,或与归档的另一个字段一起存储 过期时间,获取两者,并让应用程序了解它是否是 根据当前时间是否仍然有效。

    【讨论】:

    • 这就是为什么 redis 是一款如此出色的软件。他们知道在哪里保持简单
    • @tObi 恕我直言,当我发现我在 Redis 中需要的某些功能丢失时,这种情况经常发生
    • @tObi 不同意。每个人都会以各种错误且效率较低的方式实现相同的功能。
    【解决方案2】:

    Redis 不支持将 TTL 放在除顶部键之外的哈希上,这会使整个哈希过期。如果您使用的是分片集群,则可以使用另一种方法。这种方法不可能在所有场景中都有用,并且性能特征可能与预期的不同。还是值得一提:

    当有一个哈希时,结构基本上是这样的:

    hash_top_key
      - child_key_1 -> some_value
      - child_key_2 -> some_value
      ...
      - child_key_n -> some_value
    

    由于我们想将TTL 添加到子键,我们可以将它们移动到顶部键。重点是现在的key应该是hash_top_key和child key的组合:

    {hash_top_key}child_key_1 -> some_value
    {hash_top_key}child_key_2 -> some_value
    ...
    {hash_top_key}child_key_n -> some_value
    

    我们故意使用{} 表示法。这允许所有这些键落在同一个hash slot 中。你可以在这里阅读更多信息:https://redis.io/topics/cluster-tutorial

    现在如果我们想做同样的哈希运算,我们可以这样做:

    HDEL hash_top_key child_key_1 => DEL {hash_top_key}child_key_1
    
    HGET hash_top_key child_key_1 => GET {hash_top_key}child_key_1
    
    HSET hash_top_key child_key_1 some_value => SET {hash_top_key}child_key_1 some_value [some_TTL]
    
    HGETALL hash_top_key => 
      keyslot = CLUSTER KEYSLOT {hash_top_key}
      keys = CLUSTER GETKEYSINSLOT keyslot n
      MGET keys
    

    这里有趣的是HGETALL。首先,我们为所有子密钥获取hash slot。然后我们得到那个特定的hash slot 的键,最后我们检索值。我们需要在这里小心,因为hash slot 可能有多个n 键,也可能有我们不感兴趣但它们具有相同hash slot 的键。我们实际上可以编写一个Lua 脚本,通过执行EVALEVALSHA 命令在服务器中执行这些步骤。同样,您需要考虑这种方法在您的特定场景中的性能。

    更多参考资料:

    【讨论】:

    • 这种方法比有过期时间的简单键使用更多的内存。
    • 我可以让顶键和所有子键都过期?但是找不到如何使用所有密钥使整个哈希过期。
    • @mjs 你可以设置过期时间就可以了。 EXPIRE myHashKey 10 在 10 秒内使您的哈希过期。 redis.io/commands/expire
    • @jonlink 看起来与设置密钥相关,而不是属于哈希的密钥。编辑。我知道这就是我想要的:p ...我认为问题可能不是当您将内容添加到哈希时 ttl 没有更新?
    【解决方案3】:

    这在KeyDB 中是可能的,它是 Redis 的一个分支。因为它是一个 Fork,它与 Redis 完全兼容并且可以作为替代品。

    只需使用 EXPIREMEMBER 命令。它适用于集合、散列和排序集合。

    EXPIREMEMBER 键名子键[时间]

    你也可以使用TTL和PTTL来查看过期

    TTL 键名子键

    更多文档可在此处获得:https://docs.keydb.dev/docs/commands/#expiremember

    【讨论】:

      【解决方案4】:

      你可以在redis中使用Sorted Set来获取一个以时间戳为分数的TTL容器。 例如,每当您将事件字符串插入集合中时,您都可以将其分数设置为事件时间。 因此,您可以通过调用获取任何时间窗口的数据 zrangebyscore "your set name" min-time max-time

      此外,我们可以通过使用zremrangebyscore "your set name" min-time max-time 来删除旧事件。

      这里唯一的缺点是您必须从外部进程中进行内务处理以保持集合的大小。

      【讨论】:

        【解决方案5】:

        有一个Redisson java 框架实现散列Map 对象并支持入口TTL。它在后台使用hmapzset Redis 对象。用法示例:

        RMapCache<Integer, String> map = redisson.getMapCache('map');
        map.put(1, 30, TimeUnit.DAYS); // this entry expires in 30 days
        

        这种方法非常有用。

        【讨论】:

        • 但是如何创建地图?因为我没有找到任何教程或创建/设置方法
        • @ZoltánNémeth 在插入第一个值时自动创建的 Redis 映射。
        【解决方案6】:

        我们在这里讨论过同样的问题。

        我们有一个 Redis 哈希,一个哈希条目(名称/值对)的键,我们需要为每个哈希条目保存单独的过期时间。

        我们通过在写入哈希条目值时添加包含编码过期信息的 n 字节前缀数据来实现这一点,我们还将键设置为在写入值中包含的时间过期。

        然后,在读取时,我们解码前缀并检查是否过期。这是额外的开销,但是,读取仍然是 O(n),并且当最后一个哈希条目过期时,整个密钥将过期。

        【讨论】:

          【解决方案7】:

          关于 NodeJS 实现,我在我保存在 HASH 中的对象中添加了一个自定义 expiryTime 字段。然后在特定时间段后,我使用以下代码清除过期的 HASH 条目:

          client.hgetall(HASH_NAME, function(err, reply) {
              if (reply) {
                  Object.keys(reply).forEach(key => {
                      if (reply[key] && JSON.parse(reply[key]).expiryTime < (new Date).getTime()) {
                          client.hdel(HASH_NAME, key);
                      }
                  })
              }
          });
          

          【讨论】:

          • 您可以使用Array.filter 创建一个keys 数组以从哈希中删除,然后在一次调用中将其传递给client.hdel(HASH_NAME, ...keys),从而提高效率。
          • const keys = Object.keys(reply).filter(key =&gt; reply[key] &amp;&amp; JSON.parse(reply[key]).expiryTime &lt; Date.now()); client.hdel(HASH_NAME, ...keys);
          【解决方案8】:

          如果您的用例是您在 Redis 中缓存值并且可以容忍过时的值,但希望偶尔刷新它们以使其不会变得过时,那么一个 hacky 解决方法是在字段值并在您访问该值的任何位置处理过期。

          这使您可以继续正常使用 Redis 哈希,而无需担心其他方法可能引起的任何并发症。唯一的代价是在客户端增加一些额外的逻辑和解析。这不是一个完美的解决方案,但这是我通常所做的,因为我不需要 TTL 出于任何其他原因,而且我通常需要对缓存的值进行额外的解析。

          所以基本上它会是这样的:

          在 Redis 中:

          hash_name
          - field_1: "2021-01-15;123"
          - field_2: "2021-01-20;125"
          - field_2: "2021-02-01;127"
          

          您的(伪)代码:

          val = redis.hget(hash_name, field_1)
          timestamp = val.substring(0, val.index_of(";"))
          
          if now() > timestamp:
            new_val = get_updated_value()
            new_timestamp = now() + EXPIRY_LENGTH
            redis.hset(hash_name, field_1, new_timestamp + ";" + new_val)
            val = new_val
          else:
            val = val.substring(val.index_of(";"))
          
          // proceed to use val
          

          最大的警告 imo 是您永远不会删除字段,因此哈希会变得非常大。不确定是否有一个优雅的解决方案 - 如果感觉太大,我通常只是每隔一段时间删除一次哈希。也许您可以跟踪存储在某处的所有内容并定期删除它们(尽管此时您可能只是使用该机制手动使字段过期...)。

          【讨论】:

            【解决方案9】:

            你可以。这是一个例子。

            redis 127.0.0.1:6379> hset key f1 1
            (integer) 1
            redis 127.0.0.1:6379> hset key f2 2
            (integer) 1
            redis 127.0.0.1:6379> hvals key
            1) "1"
            2) "1"
            3) "2"
            redis 127.0.0.1:6379> expire key 10
            (integer) 1
            redis 127.0.0.1:6379> hvals key
            1) "1"
            2) "1"
            3) "2"
            redis 127.0.0.1:6379> hvals key
            1) "1"
            2) "1"
            3) "2"
            redis 127.0.0.1:6379> hvals key
            

            使用EXPIREEXPIREAT 命令。

            如果您想使哈希中的特定键过期 1 个月。这不可能。 Redis expire 命令适用于哈希中的所有键。 如果您设置每日哈希键,您可以设置一个键的生存时间。

            hset key-20140325 f1 1
            expire key-20140325 100
            hset key-20140325 f1 2
            

            【讨论】:

            • 我认为他不想使散列中的所有键过期,而是散列中所有超过1个月的键,所以只有其中一些。哪个 AFAIK 是不可能的。
            • 恕我直言,这个问题让我们假设一下。所以乔纳森从我那里得到了+1,因为他帮助了我!谢谢!
            • 如何使“f1”和“f2”过期?
            • 这不能回答问题或根本没有帮助。需要使哈希中的元素过期,而不是哈希本身。
            • @UpTheCreek 谢谢!
            【解决方案10】:

            您可以在 Redis 中以不同的方式存储键/值来实现此目的,只需在存储键时添加前缀或命名空间,例如“hset_”

            • 获取一个键/值GET hset_key等于HGET hset key

            • 添加一个键/值SET hset_key value等于HSET hset key

            • 获取所有键KEYS hset_*等于HGETALL hset

            • 获取所有 vals 应该在 2 个操作中完成,首先获取所有键 KEYS hset_* 然后获取每个键的值

            • 使用 TTL 或 expire 添加键/值,这是问题的主题:

             SET hset_key value
             EXPIRE hset_key
            

            注意KEYS 将在整个数据库中查找匹配键,这可能会影响性能,尤其是在您拥有大型数据库的情况下。

            注意:

            • KEYS 将在整个数据库中查找匹配键,这可能会影响性能,尤其是在您拥有大型数据库的情况下。而SCAN 0 MATCH hset_* 可能会更好,只要它不阻塞服务器,但在大型数据库的情况下性能仍然是一个问题。

            • 您可以创建一个新的数据库来单独存储这些您希望过期的密钥,尤其是当它们是一小组密钥时。

            感谢@DanFarrell,他强调了与 KEYS

            【讨论】:

            • 请注意这是性能特征的重大变化
            • 怎么样!如果您谈论时间复杂度,所有这些操作的时间复杂度都与hashset .. get O(1) set O(1) get all O(n)
            • "将 KEYS 视为仅应极其小心地在生产环境中使用的命令。" redis.io/commands/KEYS 。 HGETALL 是 O(n) 代表集合中的事物数量,KEYS 代表数据库中的事物数量。
            • 没错,scan 0 match namespace:* 可能会更好,只要它不阻塞服务器
            • 另外,如果这些键是小集合,则将它们分开到不同的数据库。谢谢@DanFarrell
            【解决方案11】:

            Elon Musk 很快就会把人送上月球,但我们仍然不能让 redis 上的字段过期 :(

            无论如何我想出的解决方案是:

            假设我想每 3 分钟过期一次: 所以我将数据保存在 3 个字段 0 1 2 中。 然后我以分钟为单位对当前时间执行模块%3。

            如果模块例如 == 0 所以我只使用 1 2 和 0 我删除; 然后它变为 1,所以我使用 2 和 0 并删除 1。

            我没有使用它,我没有检查它,但我只是让你知道它是可能的

            【讨论】:

              【解决方案12】:

              您可以通过psubscribe"__keyevent@&lt;DB-INDEX&gt;__:expired" 来使用Redis Keyspace Notifications。

              这样,每次密钥到期时,您都会在您的 redis 连接上发布一条消息。

              关于您的问题,基本上您使用set 创建一个临时“正常”密钥,过期时间以秒/毫秒为单位。它应该与您希望在集合中删除的密钥的名称相匹配。

              由于您的临时密钥将在过期时发布到持有"__keyevent@0__:expired" 的redis 连接,因此您可以轻松地从原始集中删除您的密钥,因为消息将包含密钥的名称。

              该页面上的一个简单示例:https://medium.com/@micah1powell/using-redis-keyspace-notifications-for-a-reminder-service-with-node-c05047befec3

              doc : https://redis.io/topics/notifications(寻找标志 xE)

              【讨论】:

                【解决方案13】:

                您可以轻松过期 Redis 哈希, 例如使用python

                import redis
                conn = redis.Redis('localhost')
                conn.hmset("hashed_user", {'name': 'robert', 'age': 32})
                conn.expire("hashed_user", 10)
                

                这将在 10 秒后使 hash hashed_user 中的所有 子键 失效

                与 redis-cli 相同,

                127.0.0.1:6379> HMSET testt username wlc password P1pp0 age 34
                OK
                127.0.0.1:6379> hgetall testt
                1) "username"
                2) "wlc"
                3) "password"
                4) "P1pp0"
                5) "age"
                6) "34"
                127.0.0.1:6379> expire testt 10
                (integer) 1
                127.0.0.1:6379> hgetall testt
                1) "username"
                2) "wlc"
                3) "password"
                4) "P1pp0"
                5) "age"
                6) "34"
                

                10 秒后

                127.0.0.1:6379> hgetall testt
                (empty list or set)
                

                【讨论】:

                • 问题是关于过期 hset 孩子不是完整的 hset
                • 不回答问题
                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2020-06-29
                • 1970-01-01
                • 1970-01-01
                • 2019-09-16
                • 1970-01-01
                • 1970-01-01
                • 2018-10-08
                相关资源
                最近更新 更多