author:徐振东

createTime:2022-05-16

updateTime:2022-06-10


2022-06-10: 培训结束

# Redis批量查询优化

​ 在讲Redis批量查询优化之前,我们先来看一个比较常见的场景,在该场景中,需要在循环体中动态生成key, 然后从redis中获取key的值, 再经过简单的组装后返回给调用者。下面是我们可能采取的代码实现方式:

RedisTemplate<String> redistempalte;
List<String> valueList = new ArrayList<>();
for(int i = 0; i < 10; i++){
    String key = "key" + i;
    valueList.add(redistempalte.opsForValue().get(key));
}
return valueList;

​ 我们知道,Redis客户端与服务端是通过 Socket进行通信的,每一次通信都要建立一次Socket连接,在 redis-server 返回结果后又断开连接。然后在该段代码中, 我们的服务在循环体中与redis-server进行交互,实际上在底层是进行频繁的创建和销毁Socket连接,而连接的创建和释放又是非常的耗费系统资源,给系统带来了比较大的负担,严重的话甚至会导致系统停顿,不可访问,无法响应客户端请求,所以我们以后在代码中需要尽量避免在循环体中访问redis。

# 解决方案

# 配置连接池

 spring:
 	redis:
     lettuce:
          pool:
            max-active: 16 # 最大连接数量
            max-idle: 8    # 最大空闲连接数量
            min-idle: 8    # 最小空闲连接数量

# 使用mGet批量获取数据

RedisTemplate<String> redistempalte;
List<String> keyList = new ArrayList<>();
for(int i = 0; i < 10; i++){
    String key = "key" + i;
    keyList.add(key);
}
List<String> valueList = redistempalte.opsForValue().multiGet(keyList);
return valueList;

​ 在该段代码中,与第一个版本不同的是,我们在循环体中动态的计算出key后,并不立即与 redis-server 交互,而是将其缓存到一个集合中,在循环结束后再通过mGet命令批量从 Redis 中获取数据,这种方式极大减少了与 Redis 的交互次数,减少 socket 连接的创建与销毁,同时也减少了 IO 次数。所以性能表现要比第一个版本好很多。

# 使用pipeline管道命令

Redis 管道技术可以在服务端未响应时,继续向服务端发送请求,并最终一次性读取所有服务端的响应。

RedisTemplate<String> redistempalte;
List<String> valueList = redistempalte.executePipelined(new SessionCallback<String>() {
    @Override
    public <K, V> Object execute(RedisOperations<K, V> operations) throws DataAccessException {
        RedisTemplate<String, Object> strRedisTemplate = (RedisTemplate<String, String>) operations;
        for(int i = 0; i < 10; i++){
            String key = "key" + i;
            strRedisTemplate.opsForValue().get(key);
        }
        return null;
    }
});
return valueList;

image-20220322172956586