如何“过期”的“ HSET”子密钥在重排?

我需要过期的所有密钥在重新散列,这是一个月以上。

173394 次浏览

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

雷迪斯的创始人安提雷斯说:

嗨,这是不可能的,要么使用不同的顶级关键 特定字段,或者与所归档的另一个字段一起存储 过期时间,同时获取这两个属性,并让应用程序理解 基于当前时间仍然有效或无效。

你可以。这里有一个例子。

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 过期命令适用于散列中的所有键。 如果设置每日散列密钥,则可以设置密钥的生存时间。

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

有一个 Redissonjava 框架,它实现带有条目 TTL 支持的散列 Map对象。它在引擎盖下使用 hmapzset Redis 对象。用法例子:

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

这种方法非常有用。

关于 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);
}
})
}
});

你可以用不同的方式在 Redis 存储键/值,只需在存储键时添加一个前缀或名称空间,例如“ hset _”

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

  • 将键/值 SET hset_key value等于 HSET hset key

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

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

  • TTL 或过期添加键/值,这是问题的主题:

 SET hset_key value
EXPIRE hset_key

注意 : KEYS将在整个数据库中查找匹配的键,这可能会影响性能,特别是如果您有大型数据库。

注:

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

  • 您可以创建一个新的数据库来分别存储这些您希望过期的密钥,特别是如果它们是一小组密钥的话。

感谢@DanFarrell 强调了与 KEYS

您可以使用 psubscribe"__keyevent@<DB-INDEX>__:expired"来使用 Redis Keyspace 通知。

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

关于您的问题,基本上您创建了一个临时的“正常”键使用 set与过期时间在 s/ms 它应该匹配的名称,您希望删除的关键在您的集合。

由于您的临时密钥将在 "__keyevent@0__:expired"过期时发布到您持有 "__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)

您可以在 reis 中使用 分类设置来获得一个以时间戳作为得分的 TTL 容器。 例如,无论何时向集合中插入事件字符串,都可以将其分数设置为事件时间。 因此,您可以通过调用 zrangebyscore "your set name" min-time max-time

而且,我们可以通过使用 zremrangebyscore "your set name" min-time max-time来删除旧的事件来做到过期。

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

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脚本,通过执行 EVALEVALSHA命令在服务器中执行这些步骤。同样,您需要考虑这种方法在特定场景中的性能。

更多参考资料:

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

我们有一个 Redis 散列,它是散列条目(名称/值对)的键,我们需要在每个散列条目上保存各个过期时间。

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

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

这是可能的在 KeyDB,这是一个分叉的红色。因为它是一个叉子它完全兼容的 Redis 和工程作为一个下降的替代品。

只需使用 EXPIREMEMBER 命令,它可以处理集合、散列和排序集合。

EXPIREMEMBER keyname subkey [ time ]

您还可以使用 TTL 和 PTTL 查看过期

TTL 键名子键

这里有更多的文档: https://docs.keydb.dev/docs/commands/#expiremember

如果你的用例是在 Redis 缓存值,并且容忍过期的值,但是想要偶尔刷新它们,这样它们就不会过期,那么一个可行的解决方案就是在字段值中包含一个时间戳,然后在你访问该值的任何地方处理过期。

这使您可以正常地继续使用 Redis 散列,而无需担心其他方法可能引起的任何并发症。唯一的开销是客户端上的一点额外的逻辑和解析。这不是一个完美的解决方案,但这是我通常会做的,因为我不需要 TTL 的任何其他原因,而且我通常需要对缓存的值进行额外的解析。

基本上就是这样:

红迪斯:

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 最大的警告是,永远不要删除字段,以便散列可以变得非常大。不知道是否有一个优雅的解决方案-我通常只是删除散列每隔一段时间,如果它觉得太大。也许您可以跟踪已经存储在某个地方的所有内容,并定期删除它们(尽管在这一点上,您可能只是使用该机制手动终止字段... ...)。

埃隆 · 马斯克很快就会把人类送上月球,而我们仍然不能让土地在重建中失效

不管怎样,我想到的解决办法是:

假设我想每3分钟过期一次: 所以我把数据保存在3个字段012中。 然后在几分钟内完成模块% 3到当前时间。

如果 example = = 0的模块 所以我只使用12和0 i 删除; 然后它变成1,所以 IM 使用2和0,并删除1。

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

  static async setCount(ip: string, count: number) {
const val = await redisClient.hSet(ip, 'ipHashField', count)
await redisClient.expire(ip, this.expireTime)
}

试试让你的钥匙过期。