java单机和集群redis兼容scan命令

scan命令简介

我们都知道Redis是单线程的,因此我们在使用一些命令(例如keys)的时候需要非常谨慎,可能一不小心就会阻塞进程,进而引起雪崩,这时候 scan命令就有了比较明显的优势;
scan命令简介:

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

命令格式如下:

SCAN cursor [MATCH pattern] [COUNT count]

其中cursor为游标,MATCH和COUNT为可选参数;

与SCAN 命令相关的命令:

与SCAN 命令相关的命令还有 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令,都是用于增量地迭代(incrementally iterate)一集元素(a collection of elements),区别在于:

1、SCAN 命令用于迭代当前数据库中的数据库键,返回的每个元素都是一个数据库键;

2、SSCAN 命令用于迭代集合键中的元素,返回的每个元素都是一个集合成员;

3、HSCAN 命令用于迭代哈希键中的键值对,返回的每个元素都是一个键值对,一个键值对由一个键和一个值组成。

4、ZSCAN 命令用于迭代有序集合中的元素(包括元素成员和元素分值),返回的每个元素都是一个有序集合元素,一个有序集合元素由一个成员(member)和一个分值(score)组成。

直接上JAVA代码
/**
     * 扫描获取key
     * @param prefixKey,redis前缀
     * @param jedisCluster,redis集群
     * @return
     */
    public Map<String,Object> scanKeyValue(String prefixKey, JedisCluster jedisCluster){
        Map<String,Object> map = new HashMap<>();
        Long start = System.currentTimeMillis();
        jedisCluster.getClusterNodes().entrySet().forEach(entry->{
            Jedis jedis = new Jedis(entry.getValue().getResource());
            ScanResult<String> scanResult = jedis.scan("0",new ScanParams().match(prefixKey+"*"));
            while (!scanResult.isCompleteIteration()) {
                for(String key :scanResult.getResult()){
                    map.put(key,jedisCluster.get(key));
                }
                scanResult = jedis.scan(scanResult.getCursor(),new ScanParams().match(prefixKey+"*"));
            }
        });
        LogUtils.info("scanKeyVlaue="+prefixKey+",time(ms):"+ (System.currentTimeMillis() - start));
        LogUtils.info("scanKeyVlaue-size:"+ map.size());
        LogUtils.info("scanKeyVlaue-result:"+ JSON.toJSONString(map));
        return map;
    }

    /**
     * 扫描获取key列表
     * @param prefixKey,redis前缀
     * @param jedisCluster 集群
     * @return
     */
    public Set<String> scanKeyList(String prefixKey, JedisCluster jedisCluster) {
        Set<String> resultList = new HashSet<>();

        jedisCluster.getClusterNodes().entrySet().forEach(entry->{
            Jedis jedis = new Jedis(entry.getValue().getResource());
            ScanResult<String> scanResult = jedis.scan("0",new ScanParams().match(prefixKey+"*"));
            while (!scanResult.isCompleteIteration()) {
                long t1 = System.currentTimeMillis();
                int start = resultList.size();
                List<String> list = scanResult.getResult();
                if(list != null) {
                    resultList.addAll(list);
                }
                int end = resultList.size();
                scanResult = jedis.scan(scanResult.getCursor(),new ScanParams().match(prefixKey+"*"));
                long t2 = System.currentTimeMillis();
//                LogUtils.info("获取" + (end-start) + "条数据,耗时: " + (t2 - t1) + "毫秒");
            }
        });
        return resultList;
    }

    /**
     * 扫描获取key,value
     * @param prefixKey,redis前缀
     * @param jedis,单机
     * @return
     */
    public Map<String,Object> scanKeyValue(String prefixKey, Jedis jedis) {
        Map<String,Object> map = new HashMap<>();
        // 游标初始值为0
        String cursor = ScanParams.SCAN_POINTER_START;
        ScanParams scanParams = new ScanParams();
        scanParams.match(prefixKey);
        scanParams.count(1000);
        while (true) {
            long t1 = System.currentTimeMillis();
            //使用scan命令获取数据,使用cursor游标记录位置,下次循环使用
            ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
            cursor = scanResult.getCursor();// 返回0 说明遍历完成
            List<String> list = scanResult.getResult();
            if(list != null) {
                for(String key :scanResult.getResult()){
                    map.put(key,jedis.get(key));
                }
            }
            long t2 = System.currentTimeMillis();
//            LogUtils.info("获取" + list.size() + "条数据,耗时: " + (t2 - t1) + "毫秒,cursor:" + cursor);
            if ("0".equals(cursor)) {
                break;
            }
        }
        return map;
    }

    /**
     * 扫描获取key列表
     * @param prefixKey,redis前缀
     * @param jedis,单机
     * @return
     */
    public Set<String> scanKeyList(String prefixKey, Jedis jedis) {
        Set<String> resultSet = new HashSet<>();
        // 游标初始值为0
        String cursor = ScanParams.SCAN_POINTER_START;
        ScanParams scanParams = new ScanParams();
        scanParams.match(prefixKey);
        scanParams.count(1000);
        while (true) {
            long t1 = System.currentTimeMillis();
            //使用scan命令获取数据,使用cursor游标记录位置,下次循环使用
            ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
            cursor = scanResult.getCursor();// 返回0 说明遍历完成
            List<String> list = scanResult.getResult();
            if(list != null) {
                resultSet.addAll(list);
            }
            long t2 = System.currentTimeMillis();
//            LogUtils.info("获取" + list.size() + "条数据,耗时: " + (t2 - t1) + "毫秒,cursor:" + cursor);
            if ("0".equals(cursor)) {
                break;
            }
        }
        return resultSet;
    }

测试类:

@RunWith(SpringRunner.class)
@Log4j2
@SpringBootTest
public class ScanTest {

	private static final String HOST = "192.168.1.XXX";
	private static final int PORT = 6379;

	@Test
	public void scanTest() {
		Jedis jedis = new Jedis(HOST, PORT);
		// 游标初始值为0
		String cursor = ScanParams.SCAN_POINTER_START;
		ScanParams scanParams = new ScanParams();
		scanParams.match("aaa-*");// 匹配以 aaa-* 为前缀的 key
		scanParams.count(1000);
		while (true){
			//使用scan命令获取数据,使用cursor游标记录位置,下次循环使用
			ScanResult<String> scanResult = jedis.scan(cursor, scanParams);
			cursor = scanResult.getCursor();// 返回0 说明遍历完成
			List<String> list = scanResult.getResult();
			long t1 = System.currentTimeMillis();
			for(int m = 0;m < list.size();m++){
				String mapentry = list.get(m);
				System.out.println(mapentry);
				//jedis.del(key, mapentry);

			}
			long t2 = System.currentTimeMillis();
			System.out.println("获取" + list.size() + "条数据,耗时: " + (t2-t1) + "毫秒,cursor:" + cursor);
			if ("0".equals(cursor)){
				break;
			}
		}
	}

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wuyongde0922

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值