이전 프로젝트에서는 특정 키를 얻기 위해 redis의 keys 명령을 사용했습니다. 이 명령은 데이터베이스가 특히 큰 경우 오랫동안 차단되므로 보안 위험이 크기 때문에 이번에는 계획하고 있습니다. 한 번 최적화해 보세요.
공식 웹사이트에서는 대신 스캔 명령을 사용할 것을 권장합니다. 그래서 사용했는데...
다음은 해당 패턴의 키를 맞추기 위해 스캔 명령어를 사용하는 코드입니다.
$redis = new Redis(); $redis->connect('localhost', 6379); $iterator = null;while ($keys = $redis->scan($iterator, 'test*')) { foreach ($keys as $key) { echo $key . PHP_EOL; } }
이 코드는 괜찮겠죠? 이는 jetbrains 회사 소유의 소프트웨어인 phpstorm의 코드 프롬프트 라이브러리에서 가져온 것입니다. 패턴 매개변수만 추가되었지만 실행 결과에 문제가 있습니다.
key 명령을 사용하면 "test1", "test2",..., "test5"의 5개 키 세트를 얻을 수 있지만 스캔을 사용할 때 아무것도 출력되지 않습니다.
……
…………
…………
다자간 분석 결과, 마침내 스캔 명령의 반환 값에 문제가 있는 것으로 확인되었습니다.
실제로 Redis 공식 문서에도 scan 명령이 반복마다 null을 반환할 수 있다고 명시되어 있지만 이는 끝을 의미하는 것이 아니며 반환된 반복의 값이 "0"일 때 완료됩니다.
따라서 위의 코드가 반복 실행될 때 키가 반환되지 않으면 $keys는 빈 배열이므로 while 루프가 자연스럽게 중단되어 출력이 없습니다.
이러한 상황은 Redis에 키가 많을 때 특히 두드러집니다. 키가 수십 또는 수백 개인 경우 이러한 상황은 거의 발생하지 않지만, 키가 수천만 개에 도달하면 이러한 상황이 거의 확실하게 발생합니다.
이런 상황의 발생을 줄이려면 스캔 기능의 세 번째 매개변수 개수를 더 큰 숫자로 설정할 수 있습니다. 그러나 이것이 이 문제에 대한 근본적인 해결책은 아닙니다. 두 가지 근본적인 해결책이 있습니다:
1.setOption
通过setOption函数来设定迭代时的行为。以下是示例代码:
$redis = new Redis(); $redis->connect('localhost', 6379); $redis->setOption(Redis::OPT_SCAN,Redis::SCAN_RETRY); $iterator = null;while ($keys = $redis->scan($iterator, 'test*')) { foreach ($keys as $key) { echo $key . PHP_EOL; } }
和上面的代码相比,只是多了个setOption的操作,这个操作的作用是啥呢?这个操作就是告诉redis扩展,当执行scan命令后,返回的结果集为空的话,函数不返回,而是直接继续执行scan命令,当然,这些步骤都是由扩展自动完成,当scan函数返回的时候,要么返回false,即迭代结束,未发现匹配模式pattern的key,要么就返回匹配的key,而不再会返回空数组了。
2.while(true)
上面那种方式是由php的扩展自动完成的,那么我们也可以换一种写法来达到相同的效果。
$redis = new Redis(); $redis->connect('localhost', 6379); $iterator = null;while (true) { $keys = $redis->scan($iterator, 'test*'); if ($keys === false) {//迭代结束,未找到匹配pattern的key return; } foreach ($keys as $key) { echo $key . PHP_EOL; } }
위 내용은 PHP가 redis 스캔 명령을 사용할 때 발생하는 함정의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!