go-redis调用SCAN命令小COUNT值无法返回全量键,如何获取亿级Redis键?
亿级Redis全键获取方案(不使用KEYS命令)
Redis
SCAN命令的COUNT参数不是单次调用返回结果的数量上限,而是告知Redis本次遍历需要扫描的字典槽位数量。当键总量大、COUNT设置过小时,单次调用返回的匹配键少,需要更多次迭代才能拉取全量,并不会出现「漏返回键」的问题,你遇到的1万键场景下COUNT=10拿不全,本质是迭代次数不够没有走完整个遍历流程。
1. 核心参数调整
COUNT参数设置为10000~100000区间:既避免单次请求扫描槽位太少导致迭代次数过多,也不会因为单次扫描量太大导致Redis阻塞、请求时延飙升。- 如果你有明确的键匹配规则,直接将匹配规则传入
MATCH参数,由Redis端过滤无效键,降低网络传输开销。
2. 架构逻辑优化
2.1 避免内存溢出
1亿量级的键如果全部存在内存中,按单键平均32字节计算至少需要3GB以上内存,极易触发程序OOM,绝对不能像示例代码那样把所有键都攒在同一个切片里:
- 采用流式处理逻辑:每拿到一个键就直接执行后续逻辑(比如写入本地文件、投递到消息队列、执行删除/统计操作等),不需要缓存全量键。
2.2 稳定性优化
- 遍历所有主节点:集群模式下必须遍历所有主节点的槽位才能拿到全量键,你的示例代码中
ForEachMaster逻辑是正确的,如果怕影响主节点业务,也可以切换到从节点执行扫描。 - 错误重试:每次迭代结束后要校验迭代器错误,如果出现超时、节点切换等异常,记录当前迭代的cursor位置,重试时从该位置继续扫描,不需要从头遍历。
- 限流保护:每扫描10万条键可以休眠1~5毫秒,避免扫描占用Redis过多CPU,影响线上正常业务请求。
- 可选去重:正常情况下SCAN迭代不会返回重复键,如果业务要求绝对无重复,可以引入轻量布隆过滤器做去重,1亿键量级的布隆过滤器仅需要几十MB内存,开销极低。
3. 修正后代码示例
// 示例为扫描后直接写入本地文件,可根据业务需求替换为其他处理逻辑 file, err := os.OpenFile("redis_keys.txt", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) if err != nil { return err } defer file.Close() writer := bufio.NewWriter(file) defer writer.Flush() err = redis.ForEachMaster(ctx, func(ctx context.Context, master *redis.Client) error { var cursor uint64 const ( match = "*" // 替换为实际匹配规则 count = 50000 batchSize = 100000 // 每攒10万条写一次文件,降低IO开销 ) batch := make([]string, 0, batchSize) iter := master.Scan(ctx, cursor, match, count).Iterator() for iter.Next(ctx) { batch = append(batch, iter.Val()) if len(batch) >= batchSize { for _, key := range batch { if _, err := writer.WriteString(key + "\n"); err != nil { return err } } batch = batch[:0] // 可选限流 // time.Sleep(time.Millisecond * 2) } } // 处理剩余的批次 for _, key := range batch { if _, err := writer.WriteString(key + "\n"); err != nil { return err } } // 检查迭代器错误 if err := iter.Err(); err != nil { return err } return nil })
内容的提问来源于stack exchange,提问作者chocopun
相关产品推荐
相关产品推荐

