Go Redis 扫描所有Key

遍历 Key

使用 KEYS prefix:* 命令可以遍历前缀为 prefix 的所有 key,但如果 redis 中有上百万或更多的 key,会变得非常慢。

go-redis 提供了 SCAN在新窗口打开 来迭代遍历前缀为 prefix 的 key:

var cursor uint64
for {
	var keys []string
	var err error
	keys, cursor, err = rdb.Scan(ctx, cursor, "prefix:*", 0).Result()
	if err != nil {
		panic(err)
	}

	for _, key := range keys {
		fmt.Println("key", key)
	}

	// 没有更多key了
	if cursor == 0 {
		break
	}
}

上面的代码也可以简化成:

iter := rdb.Scan(ctx, 0, "prefix:*", 0).Iterator()
for iter.Next(ctx) {
	fmt.Println("keys", iter.Val())
}
if err := iter.Err(); err != nil {
	panic(err)
}

集合和哈希类型

你可以使用 iterate 来迭代 redis 集合:

iter := rdb.SScan(ctx, "set-key", 0, "prefix:*", 0).Iterator()

哈希类型:

iter := rdb.HScan(ctx, "hash-key", 0, "prefix:*", 0).Iterator()
iter := rdb.ZScan(ctx, "sorted-hash-key", 0, "prefix:*", 0).Iterator()

Cluster 和 Ring

如果你使用的是 Redis ClusterRedis Ring,需要分别扫描集群每个节点:

err := rdb.ForEachMaster(ctx, func(ctx context.Context, rdb *redis.Client) error {
	iter := rdb.Scan(ctx, 0, "prefix:*", 0).Iterator()

	...

	return iter.Err()
})
if err != nil {
	panic(err)
}

删除无过期时间的 Key

你可以使用 SCAN 删除没有 TTL 的 key:

iter := rdb.Scan(ctx, 0, "", 0).Iterator()

for iter.Next(ctx) {
	key := iter.Val()

    d, err := rdb.TTL(ctx, key).Result()
    if err != nil {
        panic(err)
    }

    if d == -1 { // -1 means no TTL
        if err := rdb.Del(ctx, key).Err(); err != nil {
            panic(err)
        }
    }
}

if err := iter.Err(); err != nil {
	panic(err)
}

你也可以使用管道提高效率,请参见 示例在新窗口打开