如何在Redis中“ EXPIRE”“ HSET”子键?


107

我需要使redis哈希中所有早于1个月的密钥失效。

Answers:


117

为了使Redis保持简单这是不可能的

Redis的创建者Quoth Antirez:

嗨,不可能为该特定字段使用其他顶级密钥,或者与具有过期时间的另一个字段一起存储,同时获取两者,并让应用程序根据以下内容了解它是否仍然有效:当前时间。


8
这就是为什么redis是一款如此出色的软件。他们知道在哪里保持简单
tObi

20

Redis不支持使用TTL除顶级密钥以外的其他哈希,哈希将使整个哈希失效。如果使用分片群集,则可以使用另一种方法。这种方法并非在所有情况下都有用,并且性能特征可能与预期的有所不同。仍然值得一提:

当具有哈希时,该结构基本上如下所示:

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

由于我们要添加TTL到子键,因此可以将其移到顶部键。要点是,密钥现在应该是hash_top_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,最后获取值。我们在这里需要小心,因为可能有更多的n键,hash slot并且可能还有一些我们不感兴趣但它们具有相同键的键hash slot。实际上,我们可以编写Lua脚本来通过执行EVALor EVALSHA命令在服务器中执行这些步骤。同样,您需要考虑这种方法在特定情况下的性能。

更多参考:


与具有到期时间的简单键相比,此方法使用更多的内存。
VasileM

3

有一个Redisson java框架,该框架实现了Map具有条目TTL支持的哈希对象。它在内部使用hmapzsetRedis对象。用法示例:

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

这种方法非常有用。


但是如何创建地图?因为我找不到任何教程或创建/设置方法
-FaNaT

@ZoltánNémeth在Redis地图中,插入第一个值时会自动创建。
Nikita Koksharov


2

关于NodeJS实现,我expiryTime在保存在HASH中的对象中添加了一个自定义字段。然后,在特定时间段之后,我使用以下代码清除过期的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)在一次调用中将其传递给的方式来提高效率。
doublesharp

const keys = Object.keys(reply).filter(key => reply[key] && JSON.parse(reply[key]).expiryTime < Date.now()); client.hdel(HASH_NAME, ...keys);
doublesharp

1

您可以。这是一个例子。

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

24
我不认为他要使哈希中的所有密钥都失效,而是要使哈希中所有早于1个月的密钥失效,因此仅其中一些失效。无法使用哪个AFAIK。
UpTheCreek

1
恕我直言,问题让我们假设这一点。乔纳森(Jonathan)从我这里得到+1,因为他帮助了我!谢谢!
longliveenduro

您如何使“ f1”和“ f2”到期?
vgoklani

4
这不能回答问题或根本没有帮助。需要使散列中的元素而不是散列本身过期。
罗恩2014年

@UpTheCreek谢谢!
乔纳森·金

1

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

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

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

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

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

  • 使用TTL添加键/值或过期是问题所在:

 SET hset_key value
 EXPIRE hset_key

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

注意:

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

  • 您可以创建一个新的数据库,用于单独存储要过期的这些密钥,尤其是在密钥数量很少的情况下。

感谢@DanFarrell强调了与以下内容有关的性能问题 KEYS


1
请注意,这是对性能特征的重大更改
Daniel Farrell

怎么样!如果您谈论时间复杂性,那么所有这些操作的时间复杂度都与hashset..得到O(1)设置O(1)得到所有O(n)
Muhammad Soliman

“将KEYS视为命令,仅应格外小心地用于生产环境中。” redis.io/commands/KEYS。HGETALL O(n)用于集合中KEYS的事物数量,用于数据库中的事物数量。
丹尼尔·法瑞尔

没错,scan 0 match namespace:*只要不阻塞服务器,它可能会更好
Muhammad Soliman

1
另外,如果密钥很小,则将它们分离到不同的数据库中。感谢@DanFarrell
穆罕默德

1

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

我们有一个Redis哈希,它是哈希条目(名称/值对)的键,并且我们需要在每个哈希条目上保留各自的到期时间。

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

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


0

您可以通过psubscribe和使用Redis键空间通知"__keyevent@<DB-INDEX>__:expired"

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

关于您的问题,基本上,您使用set一个以s / ms为单位的到期时间来创建一个临时的“正常”密钥。它应与您要在集合中删除的密钥的名称匹配。

由于您的临时密钥将在"__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标志)


0

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

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

唯一的缺点是您必须通过局外人流程进行内务处理,以保持设备的大小。


-1

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

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

这将过期的所有子键的散列 hashed_user 10秒后

来自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)

6
问题是关于过期的hset孩子没满hset
thangdc94

没有回答问的问题
peteclark3
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.