Redis - 使用scan代替keys与hgetall操作

1.scan前言

当我们使用 keys * 或 hgetall 进行查询的时候会进行堵塞,导致 redis 整体不可用(因为redis是单线程的),而使用 scan 命令则不会。

从Redis v2.8开始,SCAN命令已经可用,它允许使用游标从keyspace中检索键。
对比KEYS命令,虽然SCAN无法一次性返回所有匹配结果,但是却规避了阻塞系统这个高风险,从而也让一些操作可以放在主节点上执行。

2.SCAN相关命令

  • SCAN相关命令包括SSCAN 命令、HSCAN 命令和 ZSCAN 命令,分别用于集合、哈希键及有序集等
  1. SCAN 命令用于迭代当前数据库中的数据库键。
  2. SSCAN 命令用于迭代集合键中的元素。
  3. HSCAN 命令用于迭代哈希键中的键值对。
  4. ZSCAN 命令用于迭代有序集合中的元素(包括元素成员和元素分值)。
因为 SCAN 、 SSCAN 、 HSCAN 和 ZSCAN 四个命令的工作方式都非常相似, 要记住:

SSCAN 命令、 HSCAN 命令和 ZSCAN 命令的第一个参数总是一个数据库键。

而 SCAN 命令则不需要在第一个参数提供任何数据库键 —— 因为它迭代的是当前数据库中的所有数据库键。

 

3.基本用法:

命令格式:

SCAN cursor [MATCH pattern] [COUNT count]


命令解释:scan 游标 MATCH <返回和给定模式相匹配的元素> count 每次迭代所返回的元素数量


SCAN命令是增量的循环,每次调用只会返回一小部分的元素。所以不会有KEYS命令的坑(key的数量比较多,一次KEYS查询会block其他操作)。  
SCAN命令返回的是一个游标,从0开始遍历,到0结束遍历。
通过scan中的MATCH <pattern> 参数,可以让命令只返回和给定模式相匹配的元素,实现模糊查询的效果

示例:
scan 0 match DL* count 5 
sscan myset 0 match f*

f

返回值解释:

SCAN 命令、 SSCAN 命令、 HSCAN 命令和 ZSCAN 命令都返回一个包含两个元素的 multi-bulk 回复

  • 回复的第一个元素是字符串表示的无符号 64 位整数(游标)
SCAN 命令每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。
当 SCAN 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束。
  • 回复的第二个元素是另一个 multi-bulk 回复
这个 multi-bulk 回复包含了本次被迭代的元素。

4、注意

SCAN命令不能保证每次返回的值都是有序的,另外同一个key有可能返回多次,不做区分,需要应用程序去处理。

SCAN 命令返回的每个元素都是一个数据库键。
SSCAN 命令返回的每个元素都是一个集合成员。
HSCAN 命令返回的每个元素都是一个键值对,一个键值对由一个键和一个值组成。
ZSCAN 命令返回的每个元素都是一个有序集合元素,一个有序集合元素由一个成员(member)和一个分值(score)组成。

5、Jedis实现

	/**
	 * @Description: 实现hscan dowhile形式
	 * @Author: zongx
	 * @Date: 2020/3/6
	 * @Param: pattern
	 * @return java.util.List<java.lang.String>
	*/
	public Map<String,String> hscan(final String key,final String pattern) {
		return execute(new JedisAction<Map<String,String>>() {
			@Override
			public Map<String, String> action(Jedis jedis) {
				// 游标初始值为0
				String cursor = ScanParams.SCAN_POINTER_START;
				ScanParams scanParams = new ScanParams();
				scanParams.match(pattern);
				scanParams.count(Integer.MAX_VALUE);
				Map<String, String>  results =  new HashedMap();
				do {
					ScanResult<Map.Entry<String, String>> hscanResult =
							jedis.hscan(key,  String.valueOf(cursor), scanParams);
					for (Map.Entry<String, String> en : hscanResult.getResult()) {
						results.put(en.getKey(),en.getValue());
					}
					//获取游标位置,若大于0,则代表还有数据,需要继续迭代
					cursor = hscanResult.getStringCursor();
				} while (Integer.parseInt(cursor) > 0);

			return results;
		}});

	}



	/**
	 *scan,while形式
	 * @param pattern
	 * @param count
	 * @return
	 */
	public List<String> scan(final String pattern, final int count) {
		return execute(new JedisAction<List<String>>() {
			@Override
			public List<String> action(Jedis jedis) {
				ScanParams params = new ScanParams();
				params.match(pattern);
				params.count(count);
				String cursor = "0";
				List<String> results = new ArrayList<>();
				while (true) {
					ScanResult scanResult = jedis.scan(cursor, params);
					List<String> elements = scanResult.getResult();
					if (elements != null && elements.size() > 0) {
						results.addAll(elements);
					}
					cursor = scanResult.getStringCursor();
					if ("0".equals(cursor)) {
						break;
					}
				}
				return results;
			}
		});
	}

代码还可以参考:

https://www.xttblog.com/?p=3635

工具类在下载中心可以下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值