以前のプロジェクトでは、特定のキーを取得するためにredisのkeysコマンドを使用していましたが、データベースが特に大きい場合、このコマンドは長時間ブロックされ、セキュリティ上のリスクが大きいため、今回はこのコマンドを使用する予定です。一度最適化してください。
公式ウェブサイトでは、代わりに scan コマンドを使用することを推奨しています。そこで私はそれを使用しました...
以下は、scan コマンドを使用して、対応するパターンのキーを照合するためのコードです:
$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 のコード プロンプト ライブラリから取得したもので、パターン パラメーターのみが追加されていますが、実行結果には問題があります。
keysコマンドを使用すると、設定された5つのキー「test1」、「test2」、...、「test5」を取得できますが、scanを使用すると何も出力されません。
……
…………
………………
複数者による解析の結果、最終的にscanコマンドの戻り値に問題があることが判明しました。
実際、redisの公式ドキュメントにもscanコマンドは反復ごとにnullを返す可能性があると明記されていますが、これは終了の前兆ではなく、返された反復の値が「0」になった時点で終了します。
したがって、上記のコードを反復するときに、キーが返されない場合、$keys は空の配列になるため、while ループは自然に中断され、出力はありません。
この状況は、redis に多数のキーがある場合に特に顕著です。キーが数十または数百しかない場合、この状況はめったに発生しませんが、キーが数千万に達すると、この状況はほぼ確実に発生します。
この状況の発生を減らすために、スキャン関数の 3 番目のパラメーターの数をより大きな数に設定できます。しかし、これはこの問題の根本的な解決策ではありません。根本的な解決策は 2 つあります:
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 scan コマンドを使用するときに遭遇する落とし穴の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。