php使用redis的scan命令时遇到的问题以及解决方法
1 问题以前的项目中有用到redis的keys命令来获取某些key,这个命令在数据库特别大的情况会block很长一段时间,所以有很大的安全隐患,所以这次打算优化一下。官网建议使用scan命令来代替。以下是使用scan命令来匹配相应模式的key的代码:$redis = new Redis();$redis->connect('localhost', 6379);$iterator = null
1 问题
以前的项目中有用到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
却什么也没有输出。
2 解决
经过多方分析,最终发现,是scan
命令的返回值有问题。
其实redis
的官方文档也明确说了,scan
命令每次迭代的时候,有可能返回空,但这并不是结束的标志,而是当返回的迭代的值为”0″时才算结束。
因此,上面的代码在迭代的时候,若没有key
返回,$keys
是个空数组,所以while
循环自然就中断了,所以没有任何输出。
这种情况在redis
中key
特别多的时候尤其明显,当key
只有几十个上百个的时候,很少会出现这种情况,但是当key
达到上千万,这种情况几乎必现。
要减少这种情况的出现,可以通过将scan
函数的第三个参数count
设定为一个较大的数。但这不是解决此问题的根本办法,根本办法有以下两种:
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.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;
}
}
Reference
写在最后
欢迎大家关注鄙人的公众号【麦田里的守望者zhg】,让我们一起成长,谢谢。
更多推荐
所有评论(0)