不要用!!!不要用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