SpringDataRedis批量储取数据

1,199 阅读2分钟

前一段时间在工作中遇到需要批量储存数据到Redis中这样一个需求,且需求量较大,由于Redis是一种多进程单线程的服务器,如果采用循环+set命令的方法肯定会损耗大量性能和大量时间且可能造成Redis线程阻塞,如下

批量储存Map中的数据

 Map<String, List<T>> map = new HashMap<>(); 
//处理数据 
//... 
Set<String> keySet = map.keySet(); 
//将数据储存到Redis中 
for (String key : keySet) {
     redisUtil.set(key, JSON.toJSONString(map.get(key)), 86400);//24小时有效时间
}

批量储存List中的数据

List<ScenerySpot> spotList = new ArrayList<>(); 
for (ScenerySpot scenerySpot : spotList) {
     redisUtil.set(redisKey + scenerySpot.getStationNum(),SON.toJSONString(scenerySpot),86400);
}

如上代码很简单,遍历集合放入redis,但是这样效率低,每次都要获取一次连接,连接过程表现为【请求-响应,请求-响应,请求-响应】的状态,大部分时间都在创建销毁连接了,真正执行任务的时间相对来说要少很多。

我们可以通过Redis的Pipelined通道来优化这一点,它可以实现仅建立一次连接便储存所以数据,连接过程表现为【请求,请求,请求,...,请求,响应,...,响应,响应,响应】的状态,从而实现我们批量插入的目的

我在项目中使用到的是SpringDataRedis,序列化Value方式是Jackson2JsonRedisSerializer,代码如下

 //获取序列化方式
RedisSerializer keySerializer = redisTemplate.getKeySerializer();
RedisSerializer valueSerializer = redisTemplate.getValueSerializer(); 
//建立通道 
redisTemplate.executePipelined((RedisCallback<String>) redisConnection -> {
     spotList.stream().forEach(s -> {
         //序列化Key值
         byte[] keyByte = keySerializer.serialize(redisKey + s.getStationNum());
         //序列化Value并储存
         redisConnection.set(keyByte, valueSerializer.serialize(JSON.toJSONString(s)));
         //设置过期时间24小时
         redisConnection.expire(keyByte, 86400);
     });
     return null; 
});

储存Map数据,我在应用中这一块使用通道时偶尔会因为储存时间太久导致Redis超时,后面修改了一下超时时间即可,所以要考虑一下自己的项目的数据量并合理调整超时时间

 if (map.size() > 0) {
     //将数据储存到Redis中
     Set<String> set = map.keySet();
     RedisSerializer keySerializer = redisTemplate.getKeySerializer();
     RedisSerializer valueSerializer = redisTemplate.getValueSerializer();
     try {
         //开启通道
         redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
             for (String redisKey : set) {
                 //序列化Key值
                 byte[] keyByte = keySerializer.serialize(tourismRedisKey + ":" + redisKey);
                //序列化Value并储存
                connection.set(keyByte,valueSerializer.serialize(JSON.toJSONString(map.get(redisKey))));
                //设置过期时间24小时
                connection.expire(keyByte, 86400);
             } 
             return null; 
        });
     } catch (Exception ex) {
         LOGGER.error("redis批量写入数据异常:" + ex.getMessage(), ex); 
     }
}

既然有批量存数据必然也得批量去取数据,我们可以利用RedisTimepiece的multiGet去批量获取数据,但需要前期对key值做一个设计,例如【key:参数】,我们便可以keySet查询是否存在key系列的值,存在即可取出,以下附上代码:

/**
 * 批量获取
 * @param keys
 * @param tClass 
 * @param <T>
 * @return
*/ 
public <T> List<T> getList(String keys,Class<T> tClass){ 
    if (keys == null){
        return null;
    }
    try {
        //获取所有匹配的键,防止缓存未命中
        Set<String> keySet = redisTemplate.keys(keys); 
        if (keySet == null){
            return null; 
        }
        //批量获取值
        List<Object> objects = redisTemplate.opsForValue().multiGet(keySet);
        List<T> result = new ArrayList<>();
        if (objects != null){
            for (Object object : objects) {
                //由于我是以JSON格式存,取时也通过JSON进行转化
                result.add(JSONObject.parseObject(object.toString(), tClass));
            } 
        }
        return result;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

 //方法调用
List<ScenerySpot> data = redisUtil.getList(redisKey + ":*", ScenerySpot.class);