keys
在測試環(huán)境使用redis的時候,經(jīng)常會要批量刪除key菲驴,我們但是redis并沒有提供批量刪除的命令悬荣,但是我們可以在命令行下,使用keys遍歷鍵實現(xiàn)
//批量刪除以video開頭的key
redis-cli keys video* | xargs redis-cli del
//以j秉宿,r開頭戒突,緊跟edis字符串的所有鍵
redis-cli keys [j,r]edis | xargs redis-cli del
注意:
redis是單線程架構(gòu),如果redis包含了大量的鍵描睦,執(zhí)行keys命令可能會造成redis阻塞膊存,所以一般建議不要在生產(chǎn)環(huán)境下使用keys命令。
如果非要遍歷鍵刪除的話,可以在一下三種情況使用:
(1)在一個不對外提供服務(wù)的Redis從節(jié)點上執(zhí)行隔崎,這樣不會阻塞到客戶端的請求今艺,但是會影響到主從復(fù)制。
(2)如果確認鍵值總數(shù)確實比較少爵卒,可以執(zhí)行該命令虚缎。
(3)使用scan命令漸進式的遍歷所有鍵,可以有效防止阻塞钓株。
漸進式遍歷
scan命令文檔
Redis提供了面向哈希類型实牡、集合類型、有序集合的掃描遍歷命令轴合,解決諸如hgetall创坞、smembers、zrange可能產(chǎn)生的阻塞問題受葛,對應(yīng)的命令分別是hscan题涨、sscan、zscan奔坟,它們的用法和scan基本類似携栋。
注意:
漸進式遍歷可以有效的解決keys命令可能產(chǎn)生的阻塞問題搭盾,但是scan并非完美無瑕咳秉,如果在scan的過程中如果有鍵的變化(增加、刪除鸯隅、修改)澜建,
那么遍歷效果可能會碰到如下問題:新增的鍵可能沒有遍歷到,遍歷出了重復(fù)的鍵等情況蝌以,也就是說scan并不能保證完整的遍歷出來所有的鍵炕舵,這些是我們在開發(fā)時需要考慮的。
<?php
namespace Redis;
use Redis;
class RedisTest
{
const PORT = 6379;
/**
* redis對象
*/
public $redis = null;
public function __construct()
{
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', self::PORT);
}
public function info()
{
print_r($this->redis->info());
}
/**
* 刪除前綴是test:的key
*/
public function keyDelete()
{
$pre = 'test:';
for ($i = 0; $i < 10; $i++) {
$this->redis->set($pre . "$i", "$i");
}
// Have scan retry
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
$it = NULL;
while ($arr_keys = $this->redis->scan($it, "$pre*", 5)) {
call_user_func_array([$this->redis, 'del'], $arr_keys);
echo var_export($arr_keys, true) . PHP_EOL;
}
}
}
返回結(jié)果
array (
0 => 'test:8',
)
array (
0 => 'test:1',
)
array (
0 => 'test:9',
)
array (
0 => 'test:6',
)
array (
0 => 'test:5',
)
array (
0 => 'test:0',
)
array (
0 => 'test:3',
)
array (
0 => 'test:7',
)
array (
0 => 'test:4',
)
array (
0 => 'test:2',
)
SSCAN跟畅、HSCAN咽筋、ZSCAN、SCAN命令的坑
// Have scan retry
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
$it = NULL;
while ($arr_keys = $this->redis->scan($it, "$pre*", 5)) {
call_user_func_array([$this->redis, 'del'], $arr_keys);
echo var_export($arr_keys, true) . PHP_EOL;
}
根據(jù)scan的文檔說明可知:scan命令每次迭代的時候徊件,有可能返回空奸攻,但這并不是結(jié)束的標志,而是當返回的迭代的值為”0″時才算結(jié)束虱痕。
因此睹耐,上面的代碼在迭代的時候,若沒有arr_keys返回部翘,$arr_keys是個空數(shù)組硝训,所以while循環(huán)自然就中斷了,所以沒有任何輸出。
為了避免arr_keys返回是個空數(shù)組的問題我們可以這樣解決:
解決方法一
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
告訴redis擴展窖梁,當執(zhí)行scan命令后赘风,返回的結(jié)果集為空的話,函數(shù)不返回纵刘,而是直接繼續(xù)執(zhí)行scan命令贝次。這樣當scan函數(shù)返回的時候,要么返回false彰导,即迭代結(jié)束蛔翅。
注意:SSCAN、HSCAN位谋、ZSCAN也是一樣的邏輯
解決方法二
//方式一
// Have scan retry
$this->redis->setOption(Redis::OPT_SCAN, Redis::SCAN_RETRY);
$it = NULL;
while ($arr_keys = $this->redis->scan($it, "$pre*", 5)) {
call_user_func_array([$this->redis, 'del'], $arr_keys);
echo var_export($arr_keys, true) . PHP_EOL;
}
//方式二
while (true) {
$arr_keys = $this->redis->scan($it, "$pre*");
if ($arr_keys === false) {//迭代結(jié)束山析,未找到匹配pattern的key
return;
}
call_user_func_array([$this->redis, 'del'], $arr_keys);
echo var_export($arr_keys, true) . PHP_EOL;
}