补充一下,前一段时间因为项目中使用keys导致redis卡死,于是在网上搜索解决方法,基本都是使用scan替代keys命令。然后就上线了scan替代keys。。。然后就踩坑了
结果就是redis的cpu飙升,scan操作同样是扫描表操作,会导致cpu飙高,也同样会阻塞请求
scan和keys的区别在于:keys是全表扫描,会导致阻塞,scan类似分页扫描表,通过游标接着往下扫,所以扫的数据比keys少,
相对于keys比较不容易阻塞,但不代表它不会导致阻塞,如果key数量很多,快速连续调用 SCAN
,那么对 CPU 的压力会增大,会造成cpu飙升,有性能问题。
所以keys和scan在生产环境都应该禁用,最后的解决方法还是记录缓存的key,然后直接获取key。
测试环境之所以redis使用keys和scan命令没有性能问题,是因为测试环境的的key数量很少,就算整体扫描也不会有问题,所以就是:
1、缓存key时设定过期时间,这样redis就不会有大量的未失效的key
2、线上禁止使用keys和scan
下面的就不用看了 总之keys不能用,scan也不能用!!!!!!
PHP实现 Redis使用SCAN 和 SSCAN
因为大家都知道的原因(线上禁止使用keys smembers 命令
所以用了scan 和 sscan命令获取redis中的值
取出来的数据记得去重
//使用scan匹配all 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', 1000);
$results = array_merge($results, $result);
return scanAllForMatch($pattern, $cursor, $results);
}
//使用sscan匹配 set 集合中 all key
function setScanAllForMatch($key, $pattern='*', $cursor=null, $count = 3000, $results=[]) {
if ($cursor === "0") {
return $results;
}
if ($cursor === null) {
$cursor = "0";
}
$redis = Cache::getRedis();
list($cursor, $result) = $redis->sscan($key, $cursor, 'match', $pattern, 'count', $count);
$results = array_merge($results, $result);
return setScanAllForMatch($key, $pattern, $cursor, $count, $results);
}