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

Redis里头怎么快速清理那些带特定前缀的数据,方法分享给你参考下

清理Redis中带有特定前缀的键,这是一个非常常见的运维需求,比如你想清理所有以“user_session:”开头的会话数据,或者清理某个临时项目产生的测试数据,方法有好几种,但核心是要注意两点:效率对线上服务的影响,直接使用KEYS命令是最容易想到但也是最危险的方法,我会重点介绍更优的方案。

第一种方法:绝对要避免的“踩坑”做法——KEYS命令配合DEL命令

很多人第一个想到的就是先用KEYS命令找出所有匹配的键,然后再用DEL命令删除,具体操作是这样的:

KEYS "your_prefix:*"

这个命令会列出一个所有以“your_prefix:”开头的键,然后你可能会想写个脚本,把上面命令的结果循环一遍,逐个用DEL删除。

为什么说这是坑呢? 根据Redis官方文档的明确说明,KEYS命令在执行时会扫描数据库中的所有键,如果你的Redis数据库非常庞大,有几百上千万个键,这个命令会阻塞Redis服务器很长一段时间,在这期间,Redis无法处理其他任何请求,会导致你的线上服务完全卡住,看起来就像宕机了一样。在任何生产环境(也就是正式运行的服务器)中,都严禁使用KEYS命令,它顶多只能在数据量极小的测试环境里临时用一下。

第二种方法:推荐使用的“渐进式”做法——SCAN命令配合DEL命令

为了解决KEYS命令的阻塞问题,Redis从2.8版本开始提供了SCAN命令,这是官方推荐的方法。SCAN命令的特点是非阻塞和渐进式,它不会一次性返回所有匹配的键,而是每次只返回一小部分,并给你一个游标(cursor),你拿着这个游标再次调用SCAN,它就会从上次停下的地方继续扫描,这样就把一次性的巨大压力,分摊成了多次微小的操作,对服务器的影响就变得可控了。

具体操作步骤如下:

  1. 你使用SCAN命令,初始游标设为0,并加上匹配模式MATCH
    SCAN 0 MATCH "your_prefix:*"
  2. 这个命令会返回两个东西:一个是下一次扫描需要的“新游标”,另一个是本次扫描到的、符合条件的一组键列表。
  3. 你对返回的这组键执行DEL命令进行删除,你可以用管道(pipelining)技术一次性发送多个DEL命令,减少网络往返时间,这样会更快。
  4. 你把第一步返回的“新游标”作为参数,再次执行SCAN命令。
  5. 重复这个过程,直到SCAN返回的下一个游标是“0”为止,这表示整个数据库已经扫描完毕。

你可以写一个简单的脚本(比如用Python、Shell等)来自动化这个过程,虽然总体耗时可能比一次性KEYS要长一点,但关键在于它几乎不会影响Redis的正常服务,是安全的生产环境方案,一些技术博客,比如阿里云开发者社区上,经常有文章详细介绍如何用脚本实现这个过程。

第三种方法:更高效的“内部”利器——Lua脚本

如果你觉得上面那种方法还需要外部脚本循环,有点麻烦,那么可以尝试在Redis服务器内部执行Lua脚本,Redis支持用Lua脚本一次性执行多个命令,而且保证原子性(即脚本执行过程中不会插入其他命令)。

你可以写一个Lua脚本,结合SCANDEL的逻辑,但需要注意的是,由于Lua脚本在执行时也是原子性的,如果删除的键非常多,这个脚本同样会运行较长时间,导致阻塞,更聪明的做法是模仿SCAN的思路,在Lua脚本里也实现渐进式删除:比如每次只扫描并删除100个键,然后退出脚本,再由外部程序循环调用这个脚本,直到删完为止,这种方法既利用了Lua的高效,又避免了长时间阻塞,但实现起来稍微复杂一些。

第四种方法:终极武器——使用UNLINK命令替代DEL

无论你用第二种还是第三种方法,在最后执行删除操作时,建议不要用DEL命令,而是用Redis 4.0版本引入的UNLINK命令。

DEL命令是同步删除的,它会立即释放内存,如果一次性删除很多个大键,这个释放内存的操作可能会耗时较长,从而引起服务器延迟升高。

UNLINK命令是异步删除,它会把键从键空间里立刻移除,但实际回收内存的操作会交给后台线程去慢慢处理,这样你的删除命令会立刻返回,不会阻塞后续的请求,根据Redis官方文档的描述,UNLINK是大型键删除的首选,在你用SCAN找到所有待删除的键后,应该用UNLINK key1 key2 ...来代替DEL key1 key2 ...

总结一下最佳实践:

对于线上环境的Redis,清理特定前缀数据,最稳妥、高效的做法是:使用一个外部脚本,通过SCAN命令迭代地获取所有匹配的键,并配合管道(pipelining)技术,分批使用UNLINK命令进行异步删除。

还有一些额外的提醒:

  • 备份意识:在执行大规模删除操作前,如果数据重要,务必先对Redis数据进行备份。
  • 从库处理:如果你的Redis配置了主从复制,上述命令只需要在主库上执行,删除操作会自动同步到从库。
  • 替代方案:如果你的数据结构是Hash、Set、Sorted Set等,并且给它们设置了过期时间,其实可以依赖Redis自身的过期删除机制,没必要手动去清理。

希望这些具体的方法能给你提供一个清晰的参考。

Redis里头怎么快速清理那些带特定前缀的数据,方法分享给你参考下