减少请求次数,将多条请求命令合成一次请求通过管道发给redis server,再通过回调函数一次性接收多个命令的结果,减少网络IO次数,在高并发情况下可带来明显性能提升。注意的是,redis server是单线程,多个命令合成一次请求到达redis server依然还是顺序一个个执行的,仅仅只是减少了请求IO次数。
1.作用: 让RedisTemplate进行回调,通过他们可以在同一条连接中一次执行多个redis命令。
2.SessionCalback提供了良好的封装,优先使用它。
3.RedisCallback使用的是原生RedisConnection,用起来比较麻烦,可读性差,但原生api提供的功能比较齐全。
spring boot 提供了RedisTemplate简化redis客户端的操作,引入:
org.springframework.boot
spring-boot-starter-data-redis
@Component
public class RedisTest {
@Autowired
RedisTemplate redisTemplate;
@PostConstruct
public void init() {
test1();
}
public void test1() {
List
运行结果:
pipelinedResultList=[true, true, hello world, redis]
由于 RedisCallback 的RedisConnection api都是类似于 Boolean set(byte[] key, byte[] value); key、value都需要转成字节数组这种,不推荐使用。
有的系统对延迟要求很高,那么redis管道第一次请求很慢,就需要在系统启动时进行管道的预热,保证系统启动后每次请求的低延迟。
@PostConstruct
public void init() {
long startTime = System.currentTimeMillis();
redisTemplate.executePipelined(new SessionCallback() {
@Override
public Object execute(RedisOperations operations) throws DataAccessException {
operations.hasKey((K) "");
return null;
}
});
log.info("redis初始化管道请求end,耗时:{}ms", System.currentTimeMillis() - startTime);
}
redis的读写速度十分快,所以系统的瓶颈往往是在网络通信中的延迟,redis可能会在很多时候处于空闲状态而等待命令的到达。为了解决这个问题,可以使用redis的流水线(Pipeline),流水线是一种通讯协议,类似一个队列批量执行一组命令。
集群下优化RedisPipeline操作:https://blog.csdn.net/xiaoliu598906167/article/details/82218525,自己没有实际测试,todo,先记录一下。
pipelining官方解释:
Redis服务器可以实现即使没有读取旧响应的情况下也可以发送新的请求,以这种方式可以发送多个命令到服务器而不用等待回复,最后一次获取全部的回复。这就是Redis Pipelining。
executePipelined() 的官方注释:
在一个管道连接中执行给定的动作对象,并返回结果。但是需要注意的是callback不能返回一个非null的值,callback的值将被pipeline覆盖。这个方法将使用默认的序列化和反序列化方式处理结果集。
execute和executePipelined区别图示:
execute和executePipelined都支持事务管理器,支持multi,watch,exec,discard等事务操作。
@ApiOperation(value = "multi测试接口", notes = "redis事务测试接口")
@RequestMapping(value = "/multi", method = RequestMethod.GET)
@ResponseBody
public Map testmulti() {
redisManager.setStr("wanwan", "wd小兔兔");
List list = (List) redisTemplate.execute((RedisOperations res) ->
{
//设置监控key,在exec执行前如果这个key对应的值,发生了变化,事务bu执行
//通常监控的key可以是ID,也可以是一个对象
res.watch("wanwan");
// 其实watch可以注释掉,或者设置成不监控
res.unwatch();
//开启事务,在exec执行前
res.multi();
res.opsForValue().increment("wanwan", 1);
res.opsForValue().set("wanwan2", "我的小兔兔1");
Object value2 = res.opsForValue().get("wanwan2");
System.out.println("命令在队列,所以取值为空" + value2 + "----");
res.opsForValue().set("wanwan3", "我的小兔兔3");
Object value3 = res.opsForValue().get("wanwan3");
System.out.println("命令在队列,所以取值为空" + value3 + "----");
return res.exec();
});
System.out.println(list);
Map map = new HashMap<>();
map.put("success", true);
System.out.println(";;;" + map.toString());
return map;
}
事务代码,尚未验证,todo,这里就是记录一下。
redis事务就是基于SessionCallback实现了一个监听watch,如果被监听的键发生了变化就会取消事务,没有变化就执行事务。(注意:即使被赋予了相同的值,同样视为发生变化,不予执行事务)
疑问:如果redis事务中要执行100条命令,那么watch会watch几次?
在执行前一直在watch,但是执行过程中比如开始执行100条命令中的第一条后,
就不会watch了,因为redis时单线程的,你在执行过程中,别的命令根本无法执行。
如果这个事务要执行10分钟,我在这10分钟内通过手动更改一个键的值可以不,答案是不行!因为redis单线程。
redis的事务类似mysql的串行化隔离界别,执行期间不会插入其他语句。redis使用的是乐观锁方式,这种方式允许exec前修改,这时会触发异常通知。
redis通过watch来监测数据,在执行exec前,监测的数据被其他人更改会抛出错误,取消执行。而exec执行时,redis保证不会插入其他语句来实现隔离。(可以预见到此机制如果事务中包裹过多的执行长指令,可能导致长时间阻塞其他人)