当前位置:首页 > 问答 > 正文

Redis里用游标怎么翻大堆数据,感觉挺绕但又不得不用的那些事儿

Redis里要翻大堆数据,比如几百万个键,你不可能用keys命令一把全捞出来,那样服务器可能就直接卡住了,这时候就得用游标,也就是SCAN系列命令,这事儿感觉绕,是因为它和你平时一把抓数据的习惯反着来,得一点一点“抠”。

Redis里用游标怎么翻大堆数据,感觉挺绕但又不得不用的那些事儿

(来源:Redis官方文档关于SCAN命令的说明)它的基本玩法是这样的:你第一次发一个SCAN 0命令,Redis会给你返回两个东西,一个是下一个游标的数字,比如是"38",另一部分是这次扫描到的少量键名列表,然后你得用这个返回的游标"38",再去发下一次命令SCAN 38,就这样一遍一遍,直到Redis告诉你下一个游标是"0"了,才算全部逛完,这个过程就像你拿着一个手电筒,在漆黑的仓库里照东西,每次只照亮一小片区域,记下看到的东西,然后根据指引走到下一个位置再照,直到走遍整个仓库。

Redis里用游标怎么翻大堆数据,感觉挺绕但又不得不用的那些事儿

为什么说不得不用它呢?因为keys命令是遍历,数据量一大,它会长时间霸占着Redis的主线程,别的请求就得干等着,在生产环境这是绝对不能干的,而SCAN命令是“增量式迭代”,它每次只走一小会儿,分很多次走完,这样就不会长时间阻塞服务器,服务还能正常响应别人。

Redis里用游标怎么翻大堆数据,感觉挺绕但又不得不用的那些事儿

(来源:Redis官方对阻塞命令的风险提示)感觉绕的地方有几个,第一,你没法预料到底要扫多少次才能结束,游标的数字跳来跳去,没规律,第二,它每次返回的数量不固定,可能这次给你10个键,下次给你100个,全看Redis内部怎么分配,第三,也是最让人别扭的一点,它不保证在迭代过程中,如果数据有增删,能完全精准地遍历到所有当时存在的键,也可能会返回少量重复的键,这是因为它是基于游标在Redis的底层哈希桶上跳,迭代期间如果数据变了,桶的大小可能重组,就会有些键被漏掉或者多出来,如果你要求百分百精确、一致性的遍历,SCAN不适合,它设计初衷是为了不阻塞服务,而不是精确快照。

用的时候还有几个小坑,你最好别在迭代过程中长时间停顿,因为如果停太久,Redis那边数据变化很大,这次迭代可能就变得没意义了,还有,它有好几个变种:HSCAN翻哈希类型的大字段,SSCAN翻集合,ZSCAN翻有序集合,原理都一样,就是针对特定数据结构。

(来源:Redis实战经验中常见的注意事项)虽然叫“游标”,但返回的那个数字你不能把它当成进度百分比,比如"50"不意味着完成了一半,它只是一个用于继续迭代的标识符,没有大小意义。

用游标翻数据,你得转变心态:接受它的“不精确”和“不确定”,把它当作一个在不停变化的庞大数据库里,一种既能拿到大部分数据、又不把服务器搞垮的妥协方案,写代码时,就老老实实写个循环,每次用上次的游标去调,直到归零,并且处理返回的这批数据,虽然逻辑上多了一层循环,看起来麻烦,但为了服务的稳定,这是必须付出的代价。

备用