Redis集群模式下批量操作

598 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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);
        }
    }