需求背景
redis存在多个前缀相同的key,有时需要对其进行批量查询,删除,插入。当key大了,会坑惨CPU,因为redis通过tcp对外提供服务,是要i一个多路复用的单线程,每次请求的命令都是生成一个连接,所以后面的命令会阻塞直到前面的服务处理完毕才会继续。所以呢,存在一个隐患,这个隐患也只有量起来后才会出现,当我们循环获取key的时候,一开一合,耗时啊。
解决方案
版本2.8以上,过低貌似不可以的。
是时候引入pipeline了,pipeline管道就是解决执行大量命令时、会产生大量阻塞而导致延迟的技术。
其实原理很简单,pipeline就是把所有的命令一次发过去,避免频繁的发送、接收带来的网络开销,redis在打包接收到一堆命令后,依次执行,然后把结果再打包返回给客户端。
根据项目中的缓存数据结构的实际情况,数据结构为string类型的,使用RedisTemplate的multiGet方法;数据结构为hash,使用Pipeline(管道),组合命令,批量操作redis。
所谓的无感操作。
场景1
批量插入/查询指定前缀的key的value
完整版例子
/**
* 批量获取key的value
*
* @param keys
* @return
*/
public List<Object> getMul(Collection<String> keys) {
List<String> list= (List<String>) keys;
//方法1
redisTemplate.executePipelined(new SessionCallback<Object>() {
@Override
public <K, V> Object execute(RedisOperations<K, V> redisOperations) throws DataAccessException {
for (String s : list) {
//查询
redisTemplate.opsForValue().get(s);
//插入
redisTemplate.opsForValue().set(s, "testValue");
}
return null;
}
});
//方法2
redisTemplate.executePipelined(new RedisCallback<Object>() {
@Override
public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
StringRedisConnection stringRedisConnection = (StringRedisConnection) redisConnection;
for (String s : list) {
//查询
stringRedisConnection.get(s);
//插入
stringRedisConnection.set(s, "testValue");
}
return null;
}
});
return redisTemplate.opsForValue().multiGet(keys);
}
场景2
批量删除指定前缀的key的值,
1、可用用keys方法模糊匹配后再调取delete方法干掉,但是key过大时候,匹配过慢,影响性能。
2、可以考虑分为两步去处理这个缓存
既然需要批量删除,那么肯定插入,批量插入如上所讲方式,单次插,就不用说了。但是呢,在插的时候,我们可多做一个步骤,把这些具有相同前缀的key,干入一个list集合了,代码如图所示
到此,应该明确了吧,下面要删除指定前缀的key,那么先从这个list取出所有的key,然后再调用delete方法,redisTemplate自带的delete方法支持批量删除,可无需引入管道重写那玩意。
代码如下:
这样避免模糊匹配所有,当然这种写法需要考虑一些细节,比如一致性!