前一段时间在工作中遇到需要批量储存数据到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);