我在 Golang 中使用 scan 命令,根据提供的模式获取 Redis 键。我使用的是 Redis 集群,因此为了避免键丢失,我使用了 ForEachMaster。以下是我使用的代码:
func deleteCacheKeys(ctx context.Context, pattern string, count int64, retries int) error {
redisClient := redis.NewClusterClient(&redis.ClusterOptions{
Addrs: []string{redisCluster},
})
if err := redisClient.Ping(ctx).Err(); err != nil {
return err
}
var cursor uint64
err = redisClient.ForEachMaster(ctx, func(ctx context.Context, nodeClient *redis.Client) error {
for {
keys, cursor := nodeClient.Scan(ctx, cursor, pattern, count).Val()
if len(keys) > 0 {
cmd := nodeClient.Del(ctx, keys...)
if cmd.Err() != nil {
return cmd.Err()
}
}
if cursor == 0 {
break
}
}
return nil
})
return err
}
在这个函数中,棘手的部分在于每个节点客户端扫描命令中使用的计数。当我将其设置为 1000000 时,一切正常。但是当我使用更低的数值,例如 100 甚至 100000 时,这段代码就会陷入无限循环(我等待的最长时间是 30 分钟)。当使用 1000000 作为计数时,删除模式通常需要几秒钟的时间。
之前我们用 100 万个数据都没问题,直到我们的 Redis 数据集变得非常大,这个数量也引发了无限循环。我目前正在寻找一种安全的方法来删除这些模式,而不用担心这个数量。我真的很想知道为什么会发生这种情况。
我试过把它设置为 -1,但还是卡住了。我也试过用 Unlink 代替 Del 命令,但结果还是一样。
keys, cursor := ...
您被循环内的语句的效果所绊倒。该
:=
运算符实际上将其左侧的所有变量定义为新变量,且仅作用于循环主体。所以你的陈述:
实际上等同于:
您每次拨打的电话
.Scan()
实际上都是通过 拨打的CURSOR 0
,而且cursor2
可能永远无法到达0
。以下是类似问题的说明:https://go.dev/play/p/77VFOW4ldCE
允许在不同范围内分配变量的方法之一是:
=
而不是:=
,keys
对变量进行明确声明