Redis Scan命令踩坑:500w key删除策略与实战

版权申诉
0 下载量 105 浏览量 更新于2024-09-12 收藏 378KB PDF 举报
"Redis中使用Scan命令进行大数据量操作时的踩坑经验分享,通过lua脚本实现增量迭代,避免阻塞服务器" Redis中的`SCAN`命令是一个非常重要的工具,尤其在处理大量数据时,它提供了增量迭代的能力,避免一次性加载所有数据导致服务器阻塞。在本文中,作者在尝试清理500万无过期时间的key时,遇到了一些问题,这些问题是由于对`SCAN`命令的游标机制理解不足导致的。 首先,`SCAN`命令不是一次性返回所有匹配的key,而是分批返回。它接受一个游标参数,每次迭代都会更新这个游标,直到游标返回0,表示遍历完成。这是为了确保即使在大规模数据集上也能保持服务的响应性。在lua脚本中,作者使用了`SCAN`命令来查找匹配特定模式(如`'authToken*'`)的key,并通过`COUNT`参数控制每次返回的数量。 然而,作者在第一版脚本中遇到了问题。尽管`SCAN`返回的游标用于跟踪迭代状态,但作者直接将返回的游标值赋给了lua变量`c`,并在下一次迭代中使用。这里的问题在于,`SCAN`命令的游标并非单调递增,而是根据内部数据结构变化的,因此简单地使用`tonumber`转换并再次传递可能无法正确地进行迭代。 正确的做法应该是:每次迭代时,应将上次返回的游标作为下次调用`SCAN`的输入,而不仅仅是转换和比较其值。此外,`SCAN`返回的key列表需要在当前迭代中处理,而不是保存下来等待后续迭代。这意味着对于每个key,需要立即检查其过期时间并决定是否删除。 在作者的脚本中,`TTL`命令用于获取key的剩余生存时间,如果为-1,表示key没有设置过期时间。然后,脚本会删除这些key。但是,由于没有正确处理游标,这个脚本可能无法完整遍历所有的key。 正确的lua脚本应该如下所示: ```lua local cursor = '0' local finished = false while not finished do local resp = redis.call('SCAN', cursor, 'MATCH', 'authToken*', 'COUNT', 10000) cursor = resp[1] local dataList = resp[2] for i = 1, #dataList do local d = dataList[i] local ttl = redis.call('TTL', d) if ttl == -1 then redis.call('DEL', d) end end if cursor == '0' then finished = true end end return 'all finished' ``` 这个改进后的脚本会持续迭代,直到游标变为0,表示所有符合条件的key都被检查过。同时,它避免了在lua脚本中保存和处理大量的key,减少了内存占用,提高了效率。 总结来说,`SCAN`命令是Redis处理大量数据的明智选择,但正确理解和使用它的游标机制至关重要。在编写处理大量数据的lua脚本时,必须确保每次迭代都能正确处理游标,以确保遍历完整个数据集,同时避免对服务器造成不必要的压力。