大家好,我是walker
一个从文科自学转行的程序员~
爱好编程,偶尔写写编程文章和生活
欢迎关注公众号【I am Walker】,回复“电子书”,就可以获得200多本编程相关电子书哈~
我的gitee:https://gitee.com/shen-chuhao/walker.git 里面很多技术案例!

事务处理

redis事务提供了一种“将多个命令打包, 然后一次性、按顺序地执行”的机制, 并且事务在执行的期间不会主动中断 —— 服务器在执行完事务中的所有命令之后, 才会继续处理其他客户端的其他命令。
Redis中的事务是可以视为一个队列,即我们可以通过MULTI开始一个事务,这相当于我们声明了一个命令队列。
接下来,我们向Redis中提交的每条命令,都会被排入这个命令队列。
当我们输入EXEC命令时,将触发当前事务,这相当于我们从命令队列中取出命令并执行,所以Redis中一个事务从开始到执行会经历 开始事务命令入队执行事务 三个阶段。

# 开启事务
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set name "walker"
QUEUED #会将执行命令加入队列中
127.0.0.1:6379> sadd list 1 2 3 4 5
QUEUED 
127.0.0.1:6379> get name  # 此时获取结果是获取不到的,只会显示QUEUED字段
QUEUED


# 执行队列命令
127.0.0.1:6379> exec
1) OK
2) (integer) 5
3) "walker"

  • 事务是由一系列操作组成的单个逻辑工作执行单元。特别地,因为在Redis中命令是存储在一个队列中,所以,事务中的所有命令都会按顺序执行,并且在执行事务的过程中不会被客户端发送的其它命令中断。
  • 事务是一个原子操作,事物中的命令只有两种执行结果,即全部执行或者全部不执行。如果客户端在使用MULTI命令开启事务后因为意外而没有执行EXEC命令,则事务中的所有命令都不会执行。同理,如果客户端在使用MULTI命令开启事务后执行EXEC命令,则事务中的所有命令都会执行。
  • Redis中的事务可以使用DISCARD命令来清空一个命令队列,并放弃对事务的执行。如果命令在入队时发生错误,Redis将在客户端调用EXEC命令时拒绝执行并取消事务,但是在EXEC命令执行后发生的错误,Redis将选择自动忽略。

相关指令

multi

标记一个事务块的开始。

exec

执行所有事务块的命令

discard

取消事务,放弃执行事务块内的所有命令。
127.0.0.1:6379> multi
OK
127.0.0.1:6379> set a1 name
QUEUED
127.0.0.1:6379> set a2 test
QUEUED
127.0.0.1:6379> get a2
QUEUED

# 清除队列命令
127.0.0.1:6379> discard
OK

# 之后获取就获取不到queue队列了
127.0.0.1:6379> get a2
(nil)
127.0.0.1:6379> get a1
(nil)

watch

Redis Watch 命令用于监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断

WATCH key [key ...]

springboot事务使用

开启事务

方式一

@Autowired
    private RedisTemplate redisTemplate;


    /**
    * 事务方式一
    */
    @Test
    void testTransaction(){
        //开启事务支持   
        //记得开启事务支持,但是不知道为何,我已经开启了,结果还是出现“ ERR EXEC without MULTI”,找不到原因,可能是redis版本问题
        redisTemplate.setEnableTransactionSupport(true);
        //开启事务
        redisTemplate.multi();
        ValueOperations<String, String> stringops = redisTemplate.opsForValue();
        String key="test:transaction";
        stringops.set(key,"1");
        ListOperations<String, String> listOps = redisTemplate.opsForList();
        String key1="test:transaction2";
        listOps.leftPush(key1,"1111");
        System.out.println(stringops.get(key));
        System.out.println(listOps.range(key1, 0, 10));
        //执行事务
        redisTemplate.exec();
        System.out.println(stringops.get(key));
        System.out.println(listOps.range(key1, 0, 10));

    }

方式二:使用SessionCallback

/**
    * 执行事务方式二
     * 这种方式网友们比较推荐
    */
    @Test
    public void testTransaction2(){
        redisTemplate.execute(new SessionCallback<List<Object>>(){
            @Override
            public List<Object> execute(RedisOperations operations) throws DataAccessException {
                operations.multi();
                operations.opsForValue().set("name:1","walker11");
                operations.opsForValue().set("name:2","walker22");
                operations.opsForValue().set("name:3","walker33");
                return redisTemplate.exec();
            }
        });
    }

使用discard取消事务

 /**
    * 测试取消事务
    */
    @Test
    public void testDiscard(){
        Integer flag=1;
        redisTemplate.execute(new SessionCallback<List<Object>>(){
            @Override
            public List<Object> execute(RedisOperations operations) throws DataAccessException {
                operations.multi();
                operations.opsForValue().set("name:1","walker11");
                operations.opsForValue().set("name:2","walker22");
                operations.opsForValue().set("name:3","walker33");
                if(flag.equals(1)){
                    //取消事务,经测试,在使用discard之后,不能再次执行exec(),否则会出现 ERR EXEC without MULTI错误
                    redisTemplate.discard();
                    return null;
                }
                return redisTemplate.exec();
            }
        });
    }

参考文章:https://zhuanlan.zhihu.com/p/146865185

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐