应用场景
一个使用场景是要找到redis中所有特定格式的key做处理,删除或更新。
KEYS命令
虽然KEYS可以实现这个功能,但是跟据官方文档中的说明,如果数据库中存在大量的key那么这个命令会阻塞服务数秒甚至更久的时间,对于大多数业务来说都是不可接受的。
如果用SQL表示KEYS命令类似下面语句,可以预料如果库中数据量很大,执行这个代码将是魔鬼操作
SELECT key FROM keys;
SCAN命令
于是redis在2.8版本之后新增了SCAN命令,原理其实就是把KEYS拆解成一个个小段来执行。用SQL表示就类似下面的操作,显然这样操作不管数据量多大,只要控制好LIMIT都不会太差。
redis > SCAN 0 COUNT 10 # 等同于 SELECT id, key FROM keys WHERE id>0 LIMIT 10; 100 key1 ... 109 key10 redis > SCAN 10 COUNT 10 #等同于 SELECT id, key FROM keys WHERE id>10 LIMIT 10; 110 key11 ...
模式匹配
文档中SCAN命令的完整格式是下面这个样子的。
SCAN cursor [MATCH pattern] [COUNT count]
- cursor(游标)就是上面的id>xxx的那个数字,每次传入上次扫描的结束位置
- pattern是匹配模式,比如我要筛出user前缀的key就传入user*
- count类似上面SQL中的LIMIT
但是模式匹配有个陷阱,我们执行 SCAN 0 MATCH user* COUNT 10
之后可能发现并没有返回任何东西。
这个时候你不要怀疑这个命令bug了或者redis版本有问题(只要大于2.8),因为pattern并不影响扫描过程,用SQL来解释就是无论是否指定pattern都是执行SELECT id, key FROM keys WHERE id>0 LIMIT 10;
,而不是变成SELECT id, key FROM keys WHERE id>0 AND key LIKE 'pattern' LIMIT 10;
这下懂了吧,pattern只是对SCAN的结果做筛选,如果SCAN了10条都没有这个模式出现那就返回空咯。但是你的SCAN还要按原计划继续执行下去。
应用
网上有一种删除key的命令,可以给大家参考,但是我没有执行成功,会报--scan参数错误,不知什么原因。
redis-cli --scan --pattern "ops-coffee-*" | xargs -L 2000 redis-cli del
我用PHP实现了一个简单的脚本来做这件事
function deleteByPattern($pattern, $batchCount = 200){ if(empty($pattern)){ return; } $it = NULL; do { $arr_keys = $redis->scan($it, $pattern, $batchCount); if ($arr_keys !== FALSE) { foreach($arr_keys as $str_key) { $redis->del($str_key); echo "deleted key: $str_key\n"; } } } while ($it > 0); }
参考资料
http://doc.redisfans.com/key/scan.html
https://juejin.im/post/5d06eba4e51d45775e33f55e
https://github.com/phpredis/phpredis#scan