redis事务概念

单个redis命令是原子性的,但是在批量命令操作的时候,如何才能保证其事务的完整性呢?
redis也是支持批量操作的事务功能

  1. 整个事务原子性:
    按顺序地串行化执行而不会被其他客户端发送来的命令请求所打断;
  2. 不可回滚性:一条语句执行错误(执行时错误而不是语法错误)其他语句继续执行。可能会导致数据出错。
  3. 牺牲了一致性:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面,此时的修改对外是不可见的,所以此时数据还是老数据,当执行到exec的时候,数据此时才是最新的。

redis事务语法

  1. multi -->begin 开启事务,此时不执行代码,而将代码放入队列(commands队列);
    如下,返回的也不是ok,而是返回入了队列的信息,即QUEUED,
mysec_139.196.194.253:0>multi
"OK"
mysec_139.196.194.253:0>set key1 2
"QUEUED"

此时这条【set key1并没有执行,只是放在队列里,而对比mysql,这条sql已经执行了,只不过数据库里看不出来更新,因为他现在了日志里,并没有统一提交,没有写在磁盘上】

  1. exec -->commit 提交事务,执行multi到exec之间的代码,按顺序执行,如果其中一条出现运行时错误,则继续执行其他语句。
mysec_139.196.194.253:0>exec
"ERR EXEC without MULTI"
  1. exec 总是和multi成对出现的。
  2. 没有回滚机制:不同于mysql,如果出现异常,可以选择回滚代码,而不会真正写道磁盘上,而redis没有这个机制。
  1. watch -->另外一种的try catch,rollback机制
    watch可以监听key的变化,当在事务期间,如果监听的key发生改变,则事务失效。相当于回滚到初始状态。

代码中使用

Redis list llen 原子性 redis原子性命令_语法错误

备注:语法错误和运行错误的区别

  1. 语法错误:set testKey --># 只有key没有value的语法错误,控制台是可以检测到的。
  2. 运行时错误:incr testKey --># 当testKey的value不为数值类型而报错

语法错误在写事务的时候就会直接报错,运行时错误在exec后出现,事务不回滚,其他语句正常执行

demo:for循环
@Autowird
private RedisTemplate redisTemplate;
	...
	...
	...
/*
   * 需求:秒杀项目:1000个抢20个,采用redis的单线程模型
   * redis乐观锁机制:监控key+事务+原子自增
   * */
   redisTemplate.opsForValue().set("testGoods",100);
   redisTemplate.opsForValue().set("testNum",0);//设定一个20大小的监控数据
   redisTemplate.setEnableTransactionSupport(true);//开启事务
   redisTemplate.watch("testNum");//监控key,当在事务内发生改变后,此事务不执行
   //int times = 20;
   for (int i =0;i<1000;i++) {
        int testNum = (Integer)redisTemplate.opsForValue().get("testNum");
        if (testNum<20){
             redisTemplate.multi();
             redisTemplate.opsForValue().decrement("testGoods");
             redisTemplate.opsForValue().increment("testNum");
             redisTemplate.exec();
             System.err.println("第"+i+"个");
        }else{
            System.out.println("第"+i+"个,当前价格为:null");
        }
    }
    System.out.println(redisTemplate.opsForValue().get("testGoods"));//查询还剩多少库存

其中:
testGoods是商品总量:100件
testNum是卖出的个数,此次最多卖20件
watch的是testNum,如果在事务执行中,testNum改变了,那么此条事务作废
de/inCrement是线程安全的自减和自增
打印出结果:

第0个
第1个
第2个
第3个
第4个
第5个
第6个
第7个
第8个
第9个
第10个
第11个
第12个
第13个
第14个
第15个
第16个
第17个
第18个
第19个
第20个,当前价格为:null
第21个,当前价格为:null
.........
第998个,当前价格为:null
第999个,当前价格为:null
80

可能遇到的问题:

  1. RedisTemplate increment 错误:ERR value is not an integer or out of range解决
出现位置:redisTemplate.opsForValue().increment(“testNum”);
即不能自增,因为在数据库里,他是乱码的,不是数字类型,无法进行自增。需要序列化

Redis list llen 原子性 redis原子性命令_语法错误_02


这里贴下用的序列化方式:直接用就行了


  1. 关于RedisTemplate的ERR EXEC without MULTI错误

redisTemplate没有开启事务,需要在代码理指定:
redisTemplate.setEnableTransactionSupport(true);//开启事务
这段代码也可以写在上述序列化的时候。

  1. 关于事务中(指的是开启了事务,在multi和exce的之间)的查询会报空指针的问题

关闭事务的时候是没问题的,查资料也还没有好的解决方法,就先把查询语句去掉了,只留下了更新语句。希望有知道原理的朋友评论区指导一下。