不要用!!!不要用scan 替代keys!!!Laravel 使用 scan 替代 keys
补充一下,前一段时间因为项目中使用keys导致redis卡死,于是在网上搜索解决方法,基本都是使用scan替代keys命令。然后就上线了scan替代keys。。。然后就踩坑了
结果就是redis的cpu飙升,scan操作同样是扫描表操作,会导致cpu飙高,也同样会阻塞请求
scan和keys的区别在于:keys是全表扫描,会导致阻塞,scan类似分页扫描表,通过游标接着往下扫,所以扫的数据比keys少,
相对于keys比较不容易阻塞,但不代表它不会导致阻塞,如果key数量很多,一次请求就会多次使用scan,会造成cpu飙升,也会造成redis阻塞。
所以keys和scan在生产环境都应该禁用,最后的解决方法还是记录缓存的key,然后直接获取key。
测试环境之所以redis使用keys和scan命令没有性能问题,是因为测试环境的的key数量很少,就算整体扫描也不会有问题,所以就是:
1、缓存key时设定过期时间,这样redis就不会有大量的未失效的key
2、线上禁止使用keys和scan
下面的就不用看了 总之keys不能用,scan也不能用!!!!!!
一、因为大家都知道的原因(Redis是单线程的)。而keys的复杂度为O(n),所以若我们用keys 去匹配key,如果数据量很大的话,有可能卡死了。。所以线上环境还是禁止使用keys 命令。在redis.conf文件添加如下命令就可以
rename-command KEYS ""
rename-command FLUSHALL ""
rename-command FLUSHDB ""
rename-command CONFIG ""
二、不过我们可以用scan 替代 keys
scan的特点:
- 复杂度虽然也是 O(n),但是它是通过游标分步进行的,不会阻塞线程;
- 提供 limit 参数,可以控制每次返回结果的最大条数,limit 只是对增量式迭代命令的一种提示 (hint),返回的结果可多可少;
- 同 keys 一样,它也提供模式匹配功能;
- 服务器不需要为游标保存状态,游标的唯一状态就是 scan 返回给客户端的游标整数;
- 返回的结果可能会有重复,需要客户端去重复,这点非常重要;
- 遍历的过程中如果有数据修改,改动后的数据能不能遍历到是不确定的;
- 单次返回的结果是空的并不意味着遍历结束,而要看返回的游标值是否为零
那么在laravel中怎样使用scan呢?
//使用scan命令匹配key
function scanAllForMatch($pattern, $cursor=null, $results=[]) {
if ($cursor === "0") {
return $results;
}
if ($cursor === null) {
$cursor = "0";
}
$redis = Cache::getRedis();
list($cursor, $result) = $redis->scan($cursor, 'match', $pattern, 'count', 100);
$results = array_merge($results, $result);
return scanAllForMatch($pattern, $cursor, $results);
}
参考文档:https://learnku.com/articles/25892