Redis事务详解以及实现乐观锁

介绍

Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:

  • 批量操作在发送 EXEC 命令前被放入队列缓存。
  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。
  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

一个事务从开始到执行会经历以下三个阶段:

  • 开始事务。
  • 命令入队。
  • 执行事务。

基本命令

  • multi:标记事物块的开始
  • exec:执行所有事物块的命令,执行完后会取消事物
  • discard:取消事物,放弃执行事物块的所有命令
  • watch key [key …]:监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。
  • unwatch:取消监视

示例

1、执行事物

127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a aaa
QUEUED
127.0.0.1:6379> set b bbb
QUEUED
127.0.0.1:6379> set c ccc
QUEUED
127.0.0.1:6379> exec

1) OK
2) OK
3) Ok

如果在 set b bbb 处失败,set a 已成功不会回滚,set c 还会继续执行。

事物中的异常

编译时异常

在事物块中如果有命令有语法错误,在最终执行exec命令的时候,所有命令都将不会执行。

127.0.0.1:6379> multi 
OK
127.0.0.1:6379> set aa aa
QUEUED
127.0.0.1:6379> set ss 
(error) ERR wrong number of arguments for 'set' command
127.0.0.1:6379> exec #执行时发现语法错误,将不会执行任何命令
(error) EXECABORT Transaction discarded because of previous errors.

运行时异常

在事物块中如果有命令在执行的时候,出现了错误,不会影响其他命令正常执行。

127.0.0.1:6379> set k1 v1
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incr k1 
QUEUED
127.0.0.1:6379> set k2 v2
QUEUED
127.0.0.1:6379> exec
1) (error) ERR value is not an integer or out of range #由于k1的值是字符串,所以并不能自增
2) OK #这时候第二条命令还是可以正常执行的

SpringBoot实现Redis事物

1、开启事物支持

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
    RedisSerializer<Object> serializer = redisSerializer();
    RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    redisTemplate.setKeySerializer(new StringRedisSerializer());
    redisTemplate.setValueSerializer(serializer);
    redisTemplate.setHashKeySerializer(new StringRedisSerializer());
    redisTemplate.setHashValueSerializer(serializer);
    redisTemplate.afterPropertiesSet();
    //开启事物支持
    template.setEnableTransactionSupport(true);
    return redisTemplate;
}

2、操作事物

错误操作

redisTemplate.multi();
redisTemplate.opsForValue().set("a","a");
redisTemplate.exec();

使用上面的方法会报,有知道的大佬可以在评论区留言

io.lettuce.core.RedisCommandExecutionException: ERR EXEC without MULTI

正确操作

SessionCallback<Object> callback = new SessionCallback<Object>() {
    @Override
    public Object execute(RedisOperations operations) throws DataAccessException {
      
        operations.multi();
        operations.opsForValue().set("aa","aa");
        return operations.exec();
    }
};

redisTemplate.execute(callback);

开启事物也可以在operations.multi();前面写上operations.setEnableTransactionSupport(true);

为什么redis不支持事物回滚

命令只会因为语法的问题导致失败,或是命令使用在了错误类型的key上面,也就是说,从实用性角度来说,失败的命令都是因为编程错误导致的,而这些错误应该在开发环境就应该发现,而不是应该出现在生产环境, 因为不需要对回滚进行支持,所以 Redis 的内部可以保持简单且快速。

乐观锁介绍

乐观锁是无论什么时候,始终都不会认为这条数据会被其他线程所修改,而是在修改时判断version是否跟取出来的version候一样,如果一样就进行修改,不一样则修改失败,修改失败只能继续查询这条数据然后继续修改,或者直接中断此次操作,告诉用户修改失败。

实现乐观锁

1、监控事物

127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby money 50
QUEUED

2、再开一个客户端

127.0.0.1:6379> incrby money 50
OK

3、然后到之前的客户端执行exec

127.0.0.1:6379> exec
(nil)

这时候发现提示nil,说明被监控的key如果在执行过程中,如果值发生了变化,事物将会被打断,并且会取消监视。

4、重新执行

127.0.0.1:6379> watch money
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> incrby money 50
QUEUED
127.0.0.1:6379> exec
1) (integer) 200

总结

1.事物块中的所有命令按照先进先出的方式执行。

2.redis事物当一个命令失败后,不会回滚已执行成功的命令,也不会影响后续的操作

3.使用watch必须要写在multi命令前面。

4.事务在执行过程中不会被中断,当事务队列中的所有命令都被执行完毕之后,事务才会结束。

5.带有watch命令的key如果在事物执行前,如果值发生了变化,则会中断事物操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值