
Redis HSCAN 命令详细教程HSCAN以增量游标方式遍历 Hash 的字段与值是遍历大 Hash 的标准手段。它每次调用只返回一部分数据不会像 HGETALL 那样一次性阻塞服务端。资料合集https://pan.quark.cn/s/10e98d308913、https://pan.quark.cn/s/f56bc69c5338一、概览与语法HSCAN key cursor [MATCH pattern] [COUNT count] [NOVALUES]项目说明数据类型Hash支持版本Redis 2.8.0 起NOVALUES 自 7.4 起key一个 Hash Keycursor游标首次传 0之后传上次返回的游标MATCH pattern可选glob 风格模式过滤字段名COUNT count可选每次迭代返回条数的提示值默认 10NOVALUES可选只返回字段名不返回值时间复杂度单次调用 O(1)完整遍历 O(N)ACLread、hash、slow命令标记readonly官方元数据给出的复杂度说明是每次调用 O(1)完成一次完整迭代包括足够多次调用让游标回到 0为 O(N)N 是集合中的元素数量。$TRAE_REF二、返回值结构返回一个二元数组位置内容第一个元素游标字符串形式的无符号 64 位数字第二个元素字段与值交替的数组使用 NOVALUES 时只有字段名游标返回0表示迭代结束。这不是“没有数据”的意思而是“本轮遍历已完成”。必须继续调用直到游标为 0否则会漏掉数据这是使用 SCAN 系列最常见的错误。游标本身是不透明的不要对它做加减运算或假设其递增只需原样回传。三、基础示例以下命令需要 Redis 2.8 或更新版本在测试实例的 redis-cli 中执行。文中结果是预期说明未实际连接 Redis 运行。DEL tutorial:{hscan}:user HSET tutorial:{hscan}:user name Alice city Shanghai age 30 HSCAN tutorial:{hscan}:user 0 HSCAN tutorial:{hscan}:user 0 COUNT 100 HSCAN tutorial:{hscan}:user 0 MATCH a* COUNT 100 HSCAN tutorial:{hscan}:user 0 NOVALUES COUNT 100 HSCAN tutorial:{hscan}:missing 0预期结果第一条 HSCAN 返回形如1) 0 2) 1) name 2) Alice ...的二元数组游标为0表示一次遍历即完成Hash 很小。COUNT 100提高单次返回条数。MATCH a*只返回 age 字段。NOVALUES只返回字段名。对不存在的 Key 返回1) 0 2) (empty array)即游标为 0 且结果为空数组。四、COUNT 只是提示不是保证COUNT 的默认值是 10但它只是提示值hint不保证每次返回恰好这么多条。常见误解实际情况COUNT 精确控制返回条数只是提示实际数量可能多于或少于COUNT 决定遍历总次数只能大致影响不精确COUNT 越大越好越大单次阻塞时间越长需要权衡遍历必须一次拿到全部必须循环调用直到游标为 0当 Hash 使用紧凑编码元素少且值小时Redis 会在一次调用中返回全部元素并把游标置为 0此时 COUNT 不起作用。当 Hash 转换为哈希表编码后COUNT 才会明显影响每次返回的条数。五、MATCH 是过滤而非筛选优化MATCH 在服务端对已取出的元素做模式匹配被过滤掉的元素仍然消耗了扫描成本。因此 MATCH 不能减少遍历的总工作量只能减少返回给客户端的数据量。注意事项说明模式语法glob 风格支持*、?、[abc]、[a-z]等匹配对象字段名不是字段值过滤时机取出后过滤不减少扫描量结果完整性被过滤掉的元素不会返回但遍历仍需走完大小写区分大小写如果需要对字段名做复杂筛选可以在客户端过滤避免在服务端做无谓的模式匹配。六、遍历保证与限制SCAN 系列提供的保证是有限的理解这些限制才能正确使用保证说明完整遍历从开始到结束一直存在于集合中的元素一定会被返回至少一次可能重复同一元素可能被返回多次客户端需自行去重不保证不遗漏新增元素遍历期间新增的元素可能返回也可能不返回不保证快照返回的是遍历过程中的实时状态不是某一时刻的一致快照因此 HSCAN 适合“遍历处理”而不是“精确统计”。需要精确字段总数应使用 HLEN需要一致性快照应使用 HGETALL但要评估规模。HSCAN tutorial:{hscan}:user 0 COUNT 10如果返回的游标不是0就必须把该游标作为下一次调用的参数继续执行直到返回0为止。七、客户端示例前提为已安装 redis-py 并准备好本地测试实例。importredis rredis.Redis(hostlocalhost,port6379,decode_responsesTrue)ktutorial:{hscan}:pythontry:r.delete(k)r.hset(k,mapping{ffield{i}:fvalue{i}foriinrange(100)})# 完整遍历必须循环到游标为 0cursor0seen{}whileTrue:cursor,datar.hscan(k,cursor,count20)seen.update(data)ifcursor0:breakprint(len(seen))# 100# MATCH 过滤字段名cursor0matched{}whileTrue:cursor,datar.hscan(k,cursor,matchfield1?,count50)matched.update(data)ifcursor0:breakprint(sorted(matched)[:3])# [field10, field11, field12]# 只取字段名不取值cursor,namesr.hscan(k,0,count100,no_valuesTrue)print(len(names))# 100finally:r.delete(k)r.close()JavaJedis示例使用 ScanResult 与 ScanParamstry(JedisjedisnewJedis(localhost,6379)){for(inti0;i100;i){jedis.hset(tutorial:{hscan}:java,fieldi,valuei);}Stringcursor0;ScanParamsparamsnewScanParams().count(20);MapString,StringallnewHashMap();do{ScanResultMap.EntryString,Stringresultjedis.hscan(tutorial:{hscan}:java,cursor,params);for(Map.EntryString,Stringentry:result.getResult()){all.put(entry.getKey(),entry.getValue());}cursorresult.getCursor();}while(!0.equals(cursor));System.out.println(all.size());jedis.del(tutorial:{hscan}:java);}八、典型场景与性能建议典型用途遍历大 Hash 做数据导出或迁移、按前缀批量清理字段、定期巡检采样、避免 HGETALL 阻塞主线程。相比 HGETALL 和 HKEYSHSCAN 把一次大开销拆成多次小开销是线上处理大 Key 的推荐方式。使用建议建议说明始终循环到游标为 0否则会漏数据客户端按字段名去重遍历期间可能返回重复项COUNT 取值适中过小则往返次数多过大则单次阻塞久处理期间避免修改集合增删可能导致部分元素重复或漏掉不要依赖游标数值游标不透明只做原样回传需要精确总数时用 HLENHSCAN 计数不等于字段总数九、练习、排错与总结练习新建tutorial:{hscan}:exercise写入 50 个字段用游标循环完整遍历并统计收集到的字段数确认与 HLEN 一致再用MATCH field1*遍历确认只匹配到预期字段最后用NOVALUES遍历确认返回值中只有字段名。排错要点只调用一次就停止会导致数据不全务必循环到游标为 0统计数量少于 HLEN 说明遍历未完成出现重复字段属正常应去重MATCH 没匹配到结果时检查模式语法与大小写NOVALUES 报错说明服务端版本低于 7.4返回空数组且游标为 0 说明 Key 不存在。清理使用DEL tutorial:{hscan}:user tutorial:{hscan}:missing tutorial:{hscan}:exercise。速记2.8 起支持、游标循环到 0、COUNT 只是提示、MATCH 只过滤不省扫描、可能重复需去重、NOVALUES 自 7.4 起。