以前的專案中有用到redis的keys指令來取得某些key,這個指令在資料庫特別大的情況會block很長一段時間,所以有很大的安全隱患,所以這次打算優化一下。
官網建議使用scan指令來代替。於是就用了…
以下是使用scan指令來匹配對應模式的key的程式碼:
$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的程式碼提示庫中摘出來的,只加了pattern參數,但是運行結果卻是有問題的。
使用keys指令可以得到設定的」test1″,”test2″,…..,」test5″這5個key,但是使用scan卻什麼也沒有輸出。
……
…………
##………………經過多方分析,最後發現,是scan指令的回傳值有問題。 其實redis的官方文件也明確說了,scan指令每次迭代的時候,有可能返回空,但這並不是結束的標誌,而是當返回的迭代的值為」0″時才算結束。 因此,上面的程式碼在迭代的時候,若沒有key返回,$keys是個空數組,所以while循環自然就中斷了,所以沒有任何輸出。 這種情況在redis中key特別多的時候尤其明顯,當key只有幾十個上百個的時候,很少會出現這種情況,但是當key達到上千萬,這種情況幾乎必現。 要減少這種情況的出現,可以透過將scan函數的第三個參數count設定為較大的數字。但這不是解決此問題的根本方法,根本辦法有以下兩種: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中文網其他相關文章!