前戏
在laravel项目中发现了一个隐藏较深的问题,关于操作redis的increment方法:
$key = 'exchange_'.$num;
\Cache::increment($key, 1);
该方法作用是将$key的值 +1 ,因为在增加之前没有作任何判断,当key不存在时, 内部会初始化这个key出来, 而因为这个key是没有传入过期时间的,导致这些key的过期时间是永久有效,即TTL= -1;
解决办法是在increment之前先判断是否存在这个key,存在则增加值,不存在则新建key即可
正文
问题来了: 由上面产生出来的一堆永久有效的key,如何删除呢?
有人想到: 用keys 'exchange_*'搜索出来可以吗?
不可以!KEYS操作在线上是禁止使用的!
我们知道,Redis是单线程的,如果量很大的话,keys是遍历key的,会导致阻塞,这样其他的客户端就没法连接了!那如何查找?
使用redis的scan命令 + linux的xargs
SCAN 命令
官网对于KEYS命令有一个提示: KEYS 的速度非常快,例如,Redis在一个有1百万个key的数据库里面执行一次查询需要的时间是40毫秒 。但在一个大的数据库中使用它仍然可能造成性能问题,如果你需要从一个数据集中查找特定的 KEYS, 你最好还是用 Redis 的集合结构 SETS 来代替。
但由于KEYS命令一次性返回所有匹配的key,所以,当redis中的key非常多时,对于内存的消耗和redis服务器都是一个隐患,
对于Redis 2.8以上版本给我们提供了一个更好的遍历key的命令 SCAN 该命令的基本格式:
SCAN cursor [MATCH pattern] [COUNT count]
SCAN 每次执行都只会返回少量元素,所以可以用于生产环境,而不会出现像 KEYS 或者 SMEMBERS 命令带来的可能会阻塞服务器的问题。
SCAN命令是一个基于游标的迭代器。这意味着命令每次被调用都需要使用上一次这个调用返回的游标作为该次调用的游标参数,以此来延续之前的迭代过程
当SCAN命令的游标参数(即cursor)被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。
COUNT选项
对于增量式迭代命令不保证每次迭代所返回的元素数量,我们可以使用COUNT选项, 对命令的行为进行一定程度上的调整。COUNT 选项的作用就是让用户告知迭代命令