【发布时间】:2013-05-08 20:39:05
【问题描述】:
我需要使 redis 哈希中所有超过 1 个月的键过期。
【问题讨论】:
我需要使 redis 哈希中所有超过 1 个月的键过期。
【问题讨论】:
This is not possible,为了keeping Redis simple。
Quoth Antirez,Redis 的创建者:
嗨,这是不可能的,要么为此使用不同的顶级密钥 特定字段,或与归档的另一个字段一起存储 过期时间,获取两者,并让应用程序了解它是否是 根据当前时间是否仍然有效。
【讨论】:
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 脚本,通过执行EVAL 或EVALSHA 命令在服务器中执行这些步骤。同样,您需要考虑这种方法在您的特定场景中的性能。
更多参考资料:
【讨论】:
EXPIRE myHashKey 10 在 10 秒内使您的哈希过期。 redis.io/commands/expire
这在KeyDB 中是可能的,它是 Redis 的一个分支。因为它是一个 Fork,它与 Redis 完全兼容并且可以作为替代品。
只需使用 EXPIREMEMBER 命令。它适用于集合、散列和排序集合。
EXPIREMEMBER 键名子键[时间]
你也可以使用TTL和PTTL来查看过期
TTL 键名子键
更多文档可在此处获得:https://docs.keydb.dev/docs/commands/#expiremember
【讨论】:
你可以在redis中使用Sorted Set来获取一个以时间戳为分数的TTL容器。
例如,每当您将事件字符串插入集合中时,您都可以将其分数设置为事件时间。
因此,您可以通过调用获取任何时间窗口的数据
zrangebyscore "your set name" min-time max-time
此外,我们可以通过使用zremrangebyscore "your set name" min-time max-time 来删除旧事件。
这里唯一的缺点是您必须从外部进程中进行内务处理以保持集合的大小。
【讨论】:
有一个Redisson java 框架实现散列Map 对象并支持入口TTL。它在后台使用hmap 和zset Redis 对象。用法示例:
RMapCache<Integer, String> map = redisson.getMapCache('map');
map.put(1, 30, TimeUnit.DAYS); // this entry expires in 30 days
这种方法非常有用。
【讨论】:
我们在这里讨论过同样的问题。
我们有一个 Redis 哈希,一个哈希条目(名称/值对)的键,我们需要为每个哈希条目保存单独的过期时间。
我们通过在写入哈希条目值时添加包含编码过期信息的 n 字节前缀数据来实现这一点,我们还将键设置为在写入值中包含的时间过期。
然后,在读取时,我们解码前缀并检查是否过期。这是额外的开销,但是,读取仍然是 O(n),并且当最后一个哈希条目过期时,整个密钥将过期。
【讨论】:
关于 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 => reply[key] && JSON.parse(reply[key]).expiryTime < Date.now()); client.hdel(HASH_NAME, ...keys);
如果您的用例是您在 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 是您永远不会删除字段,因此哈希会变得非常大。不确定是否有一个优雅的解决方案 - 如果感觉太大,我通常只是每隔一段时间删除一次哈希。也许您可以跟踪存储在某处的所有内容并定期删除它们(尽管此时您可能只是使用该机制手动使字段过期...)。
【讨论】:
你可以。这是一个例子。
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
如果您想使哈希中的特定键过期 1 个月。这不可能。 Redis expire 命令适用于哈希中的所有键。 如果您设置每日哈希键,您可以设置一个键的生存时间。
hset key-20140325 f1 1
expire key-20140325 100
hset key-20140325 f1 2
【讨论】:
您可以在 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)
O(n) 代表集合中的事物数量,KEYS 代表数据库中的事物数量。
scan 0 match namespace:* 可能会更好,只要它不阻塞服务器
Elon Musk 很快就会把人送上月球,但我们仍然不能让 redis 上的字段过期 :(
无论如何我想出的解决方案是:
假设我想每 3 分钟过期一次: 所以我将数据保存在 3 个字段 0 1 2 中。 然后我以分钟为单位对当前时间执行模块%3。
如果模块例如 == 0 所以我只使用 1 2 和 0 我删除; 然后它变为 1,所以我使用 2 和 0 并删除 1。
我没有使用它,我没有检查它,但我只是让你知道它是可能的
【讨论】:
您可以通过psubscribe 和"__keyevent@<DB-INDEX>__:expired" 来使用Redis Keyspace Notifications。
这样,每次密钥到期时,您都会在您的 redis 连接上发布一条消息。
关于您的问题,基本上您使用set 创建一个临时“正常”密钥,过期时间以秒/毫秒为单位。它应该与您希望在集合中删除的密钥的名称相匹配。
由于您的临时密钥将在过期时发布到持有"__keyevent@0__:expired" 的redis 连接,因此您可以轻松地从原始集中删除您的密钥,因为消息将包含密钥的名称。
doc : https://redis.io/topics/notifications(寻找标志 xE)
【讨论】:
您可以轻松过期 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。