在利用phpredis扩展使用阿里云集群版Redis时,scan命令出现了奇怪的问题。集群节点0的CPU占用率飙到100%,数据却出不来。
跟踪scan的代码后发现,scan的游标卡在了72057594037927936上(2的56次方)。因为用的是phpredis官方文档提供的do{}while()方式循环扫描,游标卡在72057594037927936上就导致了死循环。这也解释了为什么CPU会飙到100%。
而使用Redis Desktop Manager管理Redis内容,使用scan方法管理列出key时,并未出错,但游标可以达到500000000000000000以上。
由此可以判断,phpredis在读取scan返回游标时可能发生了溢出。
在几乎不可能改动phpredis扩展的代码的情况下,好在phpredis支持直接执行命令,配合阿里redis集群版的自研命令iscan进行查询还是可以满足需求。(详见https://help.aliyun.com/knowledge_detail/51306.html)
以下是phpredis直接执行redis命令的方法(官方文档:https://github.com/phpredis/phpredis#rawcommand ):
/**
* Send arbitrary things to the redis server.
* @param string $command Required command to send to the server.
* @param mixed ...$arguments Optional variable amount of arguments to send to the server.
* @return mixed
* @example
* <pre>
* $redis->rawCommand('SET', 'key', 'value'); // bool(true)
* $redis->rawCommand('GET", 'key'); // string(5) "value"
* </pre>
*/
public function rawCommand( $command, $arguments ) {}
使用类似下面的方法即可实现scan:
$it=0; // 初始化游标
$idx=0; // Redis节点ID
$scan_res = $redis->rawCommand('ISCAN', $idx, $it, 'MATCH', 'abc:*', 'COUNT', 10000);
$it=$scan_res[0];
$arr_keys = $scan_res[1];
需要注意的是:
- 游标从0开始,phpredis原本的scan是从null开始的。
- rawCommand传入游标进行scan后并不会自动修改传入变量,需要在返回结果时额外赋值。
转载请注明出处
《phpredis在阿里云集群版Redis使用scan命令遇到的坑》https://www.ywlib.com/archives/141.html (from 一闻自习室)
phpredis版本的问题。
phpredis 5.3.0 fixed.
* Use long for SCAN iteration to fix potential overflow [f13f9b7c]
(Victor Kislov)
优秀!