持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第11天,点击查看活动详情
Reids批处理操作
在大量数据需要从数据库的中查询出来,然后缓存到Redis中,如果是一条数据一条数据的写入到redis中,那么每次插入都需要和Redis进行一次通信,网络耗时非常严重。而数据插入是不怎么耗时的,需要一次性执行多条命令,来减少网络的耗时。单次一次性插入太多的数据,可能会直接占满带宽导致网络阻塞。
Redis的批处理方案
1、Redis提供了像mset、mget这样的命令,用来插入数据和取出数据。
redisTemplate.opsForValue().multiSet();
mset只能处理String类型的数据。mset命令执行的语句有原子性,发送的消息按时顺序执行。
2、PipeLine管道操作,支持的数据类型各种各样,redis中的一切类型都可以进行PipeLine。
PipeLine的执行时长会比Mset多一点,因为pipeLine会有数据插队的情况,pipeLine把发送的数据到redis,实际上是到了redis的一个队列,redis按照这个队列执行这下数据,但是其中可能会有别的数据也进入这个队列,造成可能redis的数据数据不是按照顺序。
redisTemplate.executePipelined((RedisCallback<String>) redisConnection -> {
redisConnection.set("hello".getBytes(),"world".getBytes());
redisConnection.pSetEx("hello".getBytes(),1000,"world".getBytes());
return null;
});
集群下的批处理操作
在集群模式下插入数据的时候,会根据key计算出插槽值,然后放在对应的节点上,批处理操作携带了很多个不同的key,他们会放在不同的节点上,这样又会有很多个连接,有很多个网络请求。批出来要求插入到同一个插槽下。一般采取并行slot实现(Spring boot也是采用的这种方式) ,手动实现的话需要自己自定义工具类计算出key对应的插槽值。
调用redisTemplate.opsForValue().multiSet();
默认会调用在集群模式下的并行slot实现。下面是Spring boot下在集群模式下的mset源码。
public RedisFuture<String> mset(Map<K, V> map) {
// 根据Key计算插槽
Map<Integer, List<K>> partitioned = SlotHash.partition(this.codec, map.keySet());
if (partitioned.size() < 2) {
return super.mset(map);
} else {
Map<Integer, RedisFuture<String>> executions = new HashMap();
Iterator var4 = partitioned.entrySet().iterator();
while(var4.hasNext()) {
Entry<Integer, List<K>> entry = (Entry)var4.next();
Map<K, V> op = new HashMap();
((List)entry.getValue()).forEach((k) -> {
op.put(k, map.get(k));
});
// 异步的方式,返回的是一个Future
RedisFuture<String> mset = super.mset(op);
executions.put(entry.getKey(), mset);
}
return MultiNodeExecution.firstOfAsync(executions);
}
}