一、概念:
1、介绍:Redis 事务的本质是一组命令的集合。事务支持一次执行多个命令,一个事务中所有命令都会被序列化。在事务执行过程,会按照顺序串行化执行队列中的命令,其他客户端提交的命令请求不会插入到事务执行命令序列中。
总结说:redis事务就是一次性、顺序性、排他性的执行一个队列中的一系列命令。
2、实现方法:在一个客户端操作的时候,把所有的指令一次性按照顺序排他的放在一个队列中,执行完了之后再让其他的客户端操作
二、基本操作:
1、开启事务:
设定事务的开启位置,此指令执行后,后续的所有指令均加入到事务中
multi
2、执行事务:
设定事务的结束位置,同时执行事务。与multi成对出现,成对使用。加入事务的命令暂时进入到任务队列中,并没有立即执行,只有执行exec命令才开始执行。若在事务队列中存在命令性错误(类似于java编译性错误),则执行EXEC命令时,所有命令都不会执行
exec
3、取消事务:
终止当前事务的定义,发生在multi之后,exec之前。遇到multi指令,创建队列,遇到discard指令销毁队列
discard
已经执行完毕的命令对应的数据不会自动回滚
,需要在代码中实现回滚。
三、Redis事务锁:
1、问题引入:多个客户端有可能同时操作同一组数据,并且该数据一旦被操作修改后,将不适用于继续操作。
2、解决方法:在操作之前锁定要操作的数据,一旦发生变化,终止当前操作。
(1)对 key 添加监视锁,在执行exec前如果key发生了变化,终止事务执行
watch key1 [key2……]
关系型数据库执行的锁为悲观锁,Redis的watch命令是乐观锁。
(2)取消对所有 key 的监视
unwatch
注意:
- 一但执行 EXEC 开启事务的执行后,无论事务是否执行成功, WARCH 对变量的监控都将被取消。
- 故当事务执行失败后,需重新执行WATCH命令对变量进行监控,并开启新的事务进行操作。
四、非事务型流水线:
使用事务的一个好处是底层的客户端会通过流水线来提高事务执行时的性能。下面看下不使用事务时使用流水线来进一步提升命令的执行性能。将标准的Redis连接替换成流水线连接,减少通信往返的次数。
以java为例,可以使用executePipelined
打包执行命令,一次发送给redis服务器执行。
/**
* 使用pipeline
*/
@Test
void pipeLineTest() {
List<Object> objects = redisTemplate.executePipelined(new SessionCallback<String>() {
@Override
public String execute(RedisOperations operations) throws DataAccessException {
redisTemplate.opsForList().leftPush("pipe", "k1");
redisTemplate.opsForList().rightPush("pipe", "k2");
redisTemplate.opsForList().leftPop("pipe");
redisTemplate.opsForList().rightPop("pipe");
return null;
}
});
objects.stream().forEach(System.out::println);
}