redis keys scan筛选

keys

查找符合某个条件比如前缀为cart_的key,这里我预先插入了一万条数据,在正式开始之前先说一下keys 指令的匹配规则:

keys pattern里面有3个通配符 分别是 *,?,[]

  • *:通配多个任意字符
  • ?:通配单个字符
  • []:通配数组内的某个字符

这些通配都可以组合使用,比如我插入了四个键

keys *l* //代表含有l的键
// "cdf_xl"、"abc_lele"
 
 
keys *_? //代表倒数第二个为_的键
//"1_c"
 
keys *[_xl]? //代表倒数第二个在[]里面的都会被返回
// "cdf_xl"、"abc_lele"、"1_c"

 KEYS h?llo			命中hello, hallo and hxllo等。
 KEYS h*llo			命中hllo和heeeeello等。
 KEYS h[ae]llo		命中hello和hallo,但不命中hillo。
 KEYS h[a-z]llo
 KEYS hello[0-9]
 特殊符号用"\"隔开

keys 优点呢 ,使用简单当然了,也有缺点如果数据量很大,会导致 Redis 服务卡顿,所有读写 Redis 的其它的指令都会被延后甚至会超时报错。hkeys返回哈希表key中的所有域

SCAN

这个就是从0开始查找以cart打头的大概100条数据

 scan 0 match cart* count 100

返回 "66048",可以用作下一次迭代

scan 66048 match cart* count 100

每次只返回一小部分的键,这样不会阻塞服务器,一下子在网络传输大量数据,个人感觉和前端一下子很多数据传过来导致页面崩溃,然后用分页解决差不多。但要值得注意的是使用迭代器的总时间是比keys指令要长的。如果在scan的过程中如果有键的变化(增加、 删除、 修改) ,遍历效果可能会碰到如下问题: 新增的键可能没有遍历到, 遍历出了重复的键等情况, 也就是说scan并不能保证完整的遍历出来所有的键, 我们在使用的过程中需要考虑到这一点。

  • SCAN  命令返回的每个元素都是一个数据库键。
  • SSCAN 命令返回的每个元素都是一个集合成员。
  • HSCAN 命令返回的每个元素都是一个键值对,一个键值对由一个键和一个值组成。
  • ZSCAN 命令返回的每个元素都是一个有序集合元素,一个有序集合元素由一个成员(member)和一个分值(score)组成。
redis 127.0.0.1:6379> scan 0 count 11
1) "17"
2)  1) "key:12"
    2) "key:8"
    3) "key:4"
    4) "key:14"
    5) "key:16"
    6) "key:17"
    7) "key:15"
    8) "key:10"
    9) "key:3"
    10) "key:7"
    11) "key:1"

redis 127.0.0.1:6379> scan 17 count 11
1) "0"
2) 1) "key:5"
   2) "key:18"
   3) "key:0"
   4) "key:2"
   5) "key:19"
   6) "key:13"
   7) "key:6"
   8) "key:9"
   9) "key:11"

从上面的示例可以看到, SCAN 命令的回复是一个包含两个元素的数组, 第一个数组元素是用于进行下一次迭代的新游标, 而第二个数组元素则是一个数组, 这个数组中包含了所有被迭代的元素KEY。在第二次调用 SCAN 命令时, 命令返回了游标 0 , 这表示迭代已经结束, 整个数据集(collection)已经被完整遍历过了。因此推荐在生产环境中使用

PHP代码scan实现

<?php
/**
 * 查找redis key.
 *
 * @param $redisClient //这里的redis替换为自己的redis客户端对象
 * @param null $pattern // 要匹配的规则  'test:*'
 * @param int  $count   // 每次遍历数量.count越大总耗时越短,但单次阻塞越长。 建议5000-10000。并发不高则可以调至接近1w。
 *
 * @return array
 */
public static function scanKeys($redisClient, $pattern, $count = 6000)
{
    $keyArr = [];
    $iterator = null;
    while (true) {
        // $iterator 下条数据的坐标
        $data = $redisClient->scan($iterator, $pattern, $count);
        $keyArr = array_merge($keyArr, $data ?: []);
        if (0 === $iterator) {   //迭代结束,未找到匹配
            break;
        }
        if (null === $iterator) {//"游标为null了,重置为0,继续扫描"
            $iterator = '0';
        }
    }

    return array_unique($keyArr);
}
$pattern = 'test:*';
$keyArr = $this->scanKeys($pattern);
$pipe = $redis->multi(2); //使用管道 事务=2,表示使用管道
foreach ($keyArr as $key) {
    $pipe->del($key);
}
$pipe->exec();

JAVA spring-data-redis的scan方法


  public Set<String> scan(String matchKey) {
    Set<String> keys = (Set<String>) redisTemplate
        .execute((RedisCallback<Set<String>>) connection -> {
          Set<String> keysTmp = new HashSet<>();
          Cursor<byte[]> cursor = connection
              .scan(new ScanOptions.ScanOptionsBuilder().match(matchKey).count(10000).build());
          while (cursor.hasNext()) {
            keysTmp.add(new String(cursor.next()));
          }
          return keysTmp;
        });

    return keys;
  }

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值