Redis —— 海量扫描Scan
目录一、scan扫描原理二、scan测试一、scan扫描原理如上图所示,需注意问题:1. scan与keys:复杂度都是O(n),但scan的游标分步扫描。若是生产海量扫描,keys一次获取所有正则的key,造成服务卡顿,所以避免使用keys操作2. scan命令中count:不是元素的数量,而是slot数量,从而导致每次分步遍历获取元素数量不同3. 遍历顺序:高进位加法,意思是:扩容时,从高位进
·
目录
一、scan扫描原理
如上图所示,需注意问题:
1. scan与keys:复杂度都是O(n),但scan的游标分步扫描。若是生产海量扫描,keys一次获取所有正则的key,造成服务卡顿,所以避免使用keys操作
2. scan命令中count:不是元素的数量,而是slot数量,从而导致每次分步遍历获取元素数量不同
3. 遍历顺序:高进位加法,意思是:扩容时,从高位进位,向右遍历。
4. 扩容缩容:每次扩容都是现有空间2倍,即:2^(n+1),如:110扩容后(0110、1110 _ 两者相邻)。遍历元素可能重复,尤其是扩容/缩容的情况下遍历
5. 对指定容器对象遍历:sscan、hscan、zscan
6. 扫描大key:易出现服务卡顿,redis内存大起大落
二、scan测试
下面代码是redisson的scan扫描,redis单例和集群都可以使用,若是集群扫描:每个node进行scan,结果集相加。
@Override
public Map<String, Object> scanRedisData(String key) {
// 返回结果
Map<String, Object> result = Maps.newHashMap();
// 注意:scan扫描,不能使用keys
Iterator<String> iterator = redissonClient.getKeys().getKeysByPattern("*" + key + "*", 1000).iterator();
List<String> keys = Lists.newArrayList();
while (iterator.hasNext()){
keys.add(iterator.next());
}
// 返回scan的keys
result.put("data", keys);
result.put("count", keys.size());
return result;
}
redisson源码分析:
step1:获取所有主从实例,即List<MasterSlaveEntry>
step2:每个主从实例scan,异步读取readAsync从节点数据
@Override
public Iterable<String> getKeysByPattern(String pattern, int count) {
List<Iterable<String>> iterables = new ArrayList<Iterable<String>>();
// 获取集群的MasterSlaveEntry实例(主从),并遍历
for (MasterSlaveEntry entry : commandExecutor.getConnectionManager().getEntrySet()) {
Iterable<String> iterable = new Iterable<String>() {
@Override
public Iterator<String> iterator() {
// 每个MasterSlaveEntry实例进行scan
return createKeysIterator(entry, pattern, count);
}
};
iterables.add(iterable);
}
// 所有实例结果集相加
return new CompositeIterable<String>(iterables);
}
// 每个主从实例scan
public RFuture<ListScanResult<Object>> scanIteratorAsync(RedisClient client, MasterSlaveEntry entry, long startPos,
String pattern, int count) {
if (pattern == null) {
return commandExecutor.readAsync(client, entry, StringCodec.INSTANCE, RedisCommands.SCAN, startPos, "COUNT",
count);
}
// 正则匹配查询
// 采用从节点异步读取的scan
return commandExecutor.readAsync(client, entry, StringCodec.INSTANCE, RedisCommands.SCAN, startPos, "MATCH",
pattern, "COUNT", count);
}
更多推荐
已为社区贡献3条内容
所有评论(0)