首页 > 数据库 >Redis命令之scan的用法和注意细节

Redis命令之scan的用法和注意细节

时间:2024-08-12 22:37:50浏览次数:14  
标签:count String scan Redis 用法 cursor key scanParams

背景

Redis提供了scan命令,用于增量迭代获取db里的key。

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

其中SCANMATCHCOUNT为命令关键字;
cursor为游标,如果为0表示起始,每次执行命令会返回新的cursor,可用于下次命令的增量迭代;
pattern为模式,即匹配规则,如Match *表示匹配所有key,sys:*表示匹配sys:开头的所有key;
count为数量,表示每次命令返回多少个key;

注意:
MATCH pattern为可选参数,默认为Match *,匹配所有key;
COUNT count为可选参数,默认为Count 10,返回10个key;

特别注意:
COUNT count,是一种提示(hint),如Count 10表示期望返回10个key,大多数情况是有效的,但不一定100%返回10个key。

本地测试

执行以下Redis命令,新建5个key:

set a 1
set b 2
set c 3
set d 4
set e 5

查看当前db的key总数,执行dbszie
(integer) 5

查看所有key,执行keys *

1) "d"
2) "a"
3) "b"
4) "c"
5) "e"

注:
这里本机开发环境,且确定key总数很少,因此执行key *
线上环境、key总数较大时应谨慎操作,禁用key *避免命令耗时过长影响其它命令执行,用轻量的scan命令代替。

执行scan 0 match * count 1

1) "6"
2) 1) "b"

返回下个游标cursor为6,返回了1个key:b

根据返回的cursor,继续执行scan 6 match * count 1

1) "5"
2) 1) "d"
   2) "a"

返回下个游标cursor为5,返回了2个key:da

count 1返回了2个key,因此验证了Count count不一定100%返回count个key。

根据返回的cursor,继续执行scan 5 match * count 1

1) "7"
2) 1) "c"

返回下个游标cursor为7,返回了1个key:c

根据返回的cursor,继续执行scan 7 match * count 1

1) "0"
2) 1) "e"

返回下个游标cursor为0,返回了1个key:e
返回cursor为0,表示迭代结束,一共返回了5个key,符合预期。

项目实战

根据Redis的scan命令格式,定义java接口:

`List<String> scan(String keyPattern, int scanSize);`

其中:
keyPattern为命令里的pattern值,scanSizecount值;
返回List<String>表示命令迭代执行完成后所有的返回的key列表;

使用jedis依赖包,接口实现:

@Override
public List<String> scan(String keyPattern, int scanSize) {
    List<String> keys = new ArrayList<>();
    try (ShardedJedis shardedJedis = pool.getResource()) {
        try (Jedis jedis = shardedJedis.getAllShards().iterator().next()) {
            ScanParams scanParams = new ScanParams();
            scanParams.match(keyPattern);
            scanParams.count(scanSize);
            String cursor = ScanParams.SCAN_POINTER_START;
            while (true) {
                ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
                cursor = scanResult.getStringCursor();
                List<String> list = scanResult.getResult();
                if (list.size() > 0) {
                    keys.addAll(list);
                }

                if ("0".equals(cursor)) {
                    break;
                }
            }
        }
    }
    return keys;
}

项目实战中,除了获取key,还有其它需求,如:
每次迭代获取一批key做一些自定义操作;
删除某个pattern的所有key;

接口定义:

void scan(String keyPattern, int scanSize, RedisKeyCallback callback);

void scan(String keyPattern, int scanSize, RedisBatchKeysCallback callback);

int scanAndDelete(String keyPattern, int scanSize, int deleteSize);

public interface RedisKeyCallback {
    void doCallback(Jedis jedis, String key);
}

public interface RedisBatchKeysCallback {
    void doCallback(Jedis jedis, List<String> keys);
}

接口实现:

@Override
public void scan(String keyPattern, int scanSize, RedisKeyCallback callback) {
    try (ShardedJedis shardedJedis = pool.getResource()) {
        try (Jedis jedis = shardedJedis.getAllShards().iterator().next()) {
            ScanParams scanParams = new ScanParams();
            scanParams.match(keyPattern);
            scanParams.count(scanSize);
            String cursor = ScanParams.SCAN_POINTER_START;
            while (true) {
                ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
                cursor = scanResult.getStringCursor();
                List<String> list = scanResult.getResult();
                if (!CollectionUtils.isEmpty(list)) {
                    for (String key : list) {
                        callback.doCallback(jedis, key);
                    }
                }

                if ("0".equals(cursor)) {
                    break;
                }
            }
        }
    }
}

@Override
public void scan(String keyPattern, int scanSize, RedisBatchKeysCallback callback) {
    try (ShardedJedis shardedJedis = pool.getResource()) {
        try (Jedis jedis = shardedJedis.getAllShards().iterator().next()) {
            ScanParams scanParams = new ScanParams();
            scanParams.match(keyPattern);
            scanParams.count(scanSize);
            String cursor = ScanParams.SCAN_POINTER_START;
            while (true) {
                ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
                cursor = scanResult.getStringCursor();
                List<String> list = scanResult.getResult();
                if (!CollectionUtils.isEmpty(list)) {
                    callback.doCallback(jedis, list);
                }

                if ("0".equals(cursor)) {
                    break;
                }
            }
        }
    }
}

@Override
public int scanAndDelete(String keyPattern, int scanSize, int deleteSize) {
    int count = 0;
    try (ShardedJedis shardedJedis = pool.getResource()) {
        try (Jedis jedis = shardedJedis.getAllShards().iterator().next()) {
            ScanParams scanParams = new ScanParams();
            scanParams.match(keyPattern);
            scanParams.count(scanSize);
            String cursor = ScanParams.SCAN_POINTER_START;
            List<String> deleteKeys = new ArrayList<>(deleteSize);
            while (true) {
                ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
                cursor = scanResult.getStringCursor();
                List<String> list = scanResult.getResult();
                if (list.size() > 0) {
                    for (int i = 0; i < list.size(); i++) {
                        count++;
                        String scanKey = list.get(i);
                        deleteKeys.add(scanKey);
                        if (deleteKeys.size() >= deleteSize || i >= list.size() - 1) {
                            jedis.del(deleteKeys.toArray(new String[0]));
                            deleteKeys.clear();
                        }
                    }
                }

                if ("0".equals(cursor)) {
                    break;
                }
            }
        }
    }
    return count;
}

参考

Redis命令文档 http://doc.redisfans.com/key/scan.html

标签:count,String,scan,Redis,用法,cursor,key,scanParams
From: https://www.cnblogs.com/cdfive2018/p/18355288

相关文章

  • @ComponentScan
    @ComponentScan是一个注解,用于Spring框架,它允许开发者指定Spring应该扫描哪个包或包下的子包来寻找组件(如@Component、@Service、@Repository等注解标注的类)。通过使用@ComponentScan,开发者可以自动化地注册这些组件,使得它们能够被Spring容器管理。语法@ComponentScan注解的......
  • NoSQL之Redis配置与优化
    Redis简介Redis(RemoteDictionaryServer,远程字典型)是一个开源的、使用C语言编写的NoSQL数据库。Redis基于内存运行并支持持久化,采用key-value(键值对)的存储形式,是目前分布式架构中不可或缺的一环Redis优点:具有极高的数据读写速度,数据读取的速度最高可达到110000次/......
  • NoSQL 之Redis集群
    Redis集群的实现方法一般有客户端分片、代理分片和服务器端分片三种解决方案目录一:Redis集群方式主从复制哨兵模式集群模式(Redis-Cluster)二、数据分片方式客户端分片代理分片服务器分片三、故障处理故障转移多slave选举四、Redis群集部署1:安装redis(每个节点......
  • Redis与接口自动化
    1.Redis与接口自动化测试框架的集成使用Python操作Redis需要导入相应的客户端库,例如:pip install redisimportredis2.初始化Redis连接在接口自动化测试框架的初始化过程中,可以添加连接Redis的代码,确保测试过程中能够与Redis建立连接classTestFramework......
  • redis的更新策略以及淘汰策略
    redis更新策略1.先更新缓存再更新数据库:在双写场景下,很容易出现一致性问题,在读写场景下,小概率出现一致性问题,所以Pass。2.先删除缓存再更新数据库:在双写场景下,不会出现一致性问题,在读写场景下,很容易出现一致性问题,所以Pass。3.先更新数据库再更新缓存:在双写场景下,很容易出现一......
  • 一文读懂分布式爬虫利器Scrapy-Redis:源码解析、队列管理与去重策略
    分布式利器Scrapy-Redis原理Scrapy-Redis库已经为我们提供了Scrapy分布式的队列、调度器、去重等功能,其GitHub地址为:https://github.com/rmax/scrapy-redis。本节课我们深入掌握利用Redis实现Scrapy分布式的方法,并深入了解Scrapy-Redis的原理。1.获取源码......
  • 一文读懂分布式爬虫利器Scrapy-Redis:源码解析、队列管理与去重策略
    分布式利器Scrapy-Redis原理Scrapy-Redis库已经为我们提供了Scrapy分布式的队列、调度器、去重等功能,其GitHub地址为:https://github.com/rmax/scrapy-redis。本节课我们深入掌握利用Redis实现Scrapy分布式的方法,并深入了解Scrapy-Redis的原理。1.获取源码可以......
  • 手把手教你实现Scrapy-Redis分布式爬虫:从配置到最终运行的实战指南
    1.scrapy-redis的环境准备pipinstallscrapy-redis安装完毕之后确保其可以正常导入使用即可。2.实现接下来我们只需要简单的几步操作就可以实现分布式爬虫的配置了。2.1修改Scheduler在前面的课时中我们讲解了Scheduler的概念,它是用来处理Request、Item等对象的调度......
  • Redis 实现简单排行榜功能 | 实战案例
    一、业务场景口算小程序,用户完成口算并获得满分,根据耗时长短进行rank排名,耗时越短,排名越高。主要有以下功能:1.用户数据Mysql与Redis同步:使用一个redishash用来保存用户基本信息,field为userId,value为用户基础数据(本案例为昵称);用户修改昵称时,同步更新hash中对应userId的nickn......
  • Python Redis Stream【生产者=》消费者模式】
    1importredis2importtime3fromtypingimportDict,List,Tuple,Any,Optional45fromconfig.modelimportsettings6frompydanticimportBaseModel789classStreamMessage(BaseModel):10message_id:str11message_da......