redis scan php没有游标,redis中通过scan命令来搞定keys命令不可用的场景

一、概述

在redis中,我们经常通过keys命令,来查看当前redis中键,它是一个相当不错的命令,格式如下:KEYS pattern

比如:

KEYS * 匹配数据库中所有 key 。

KEYS fi?d 匹配单个字符,如find、fied等

KEYS find*.wang 匹配一个或多个字符,比findme.wang

KEYS fin[de]me 匹配指定的字符,即匹配findme或fineme。

最近有个需求,需要删除redis中旧数据,其中键中含有日期,如下:

d3b906a588b476f7eb513e2d18c06403.png

我想把3天前的数据,都删除,怎么搞呢?

首先,我想到的是通过key hash.out.*,匹配出该类型的所有key,然后提取key中的日期,根据提取的日期做删除操作。

发现既然报错:

(error) ERR unknown command 'key'

原来当数据规模较大时使用,KEYS命令,会严重影响Redis性能,也非常危险,于是被禁用掉了。

既然key命令没了,只能能够scan命令来解决。

二、scan命令

SCAN 命令是一个基于游标的迭代器(cursor based iterator),每次被调用之后, 都会向用户返回一个‍‍新的游标‍‍, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。当 SCAN 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。

scan命令格式如下:SCAN cursor [MATCH pattern] [COUNT count]

参数描述:

cursor,即游标;

MATCH pattern 匹配的格式,和keys命令匹配类似;

COUNT count 就是让用户告知迭代命令, 在每次迭代中应该从数据集里返回多少元素

三、解决方案

通过scan命令的了解,我们是可以通过scan命令来解决问题的。命令格式如下:scan 0 MATCH hash.out.*

这样就可以匹配出一hash.out.开头的key

php的实现如下:$index    = 0;

$matchKey = 'hash.out.*';

$data     = $redis->scan($index, ['MATCH', $matchKey]);

if (empty($data)) {

return;

}

$nextId = array_shift($data);

$data   = array_shift($data);

while ($nextId != $index) {

//删除旧数据

foreach ($data as $curKey) {

if (empty($curKey)) {

continue;

}

$curDay = end(explode('.', $curKey));

if ($twoDaysAgo > $curDay) {

//删除当前key

$redis->del($curKey);

}

}

$data = $redis->scan($nextId, ['MATCH', $matchKey]);

if (empty($data)) {

break;

}

$nextId = array_shift($data);

$data   = array_shift($data);

}