redis事务概念
单个redis命令是原子性的,但是在批量命令操作的时候,如何才能保证其事务的完整性呢?
redis也是支持批量操作的事务功能
- 整个事务原子性:
按顺序地串行化执行而不会被其他客户端发送来的命令请求所打断; - 不可回滚性:一条语句执行错误(执行时错误而不是语法错误)其他语句继续执行。可能会导致数据出错。
- 牺牲了一致性:将多个命令入队到事务中,接到这些命令并不会立即执行,而是放到等待执行的事务队列里面,此时的修改对外是不可见的,所以此时数据还是老数据,当执行到exec的时候,数据此时才是最新的。
redis事务语法
- 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已经执行了,只不过数据库里看不出来更新,因为他现在了日志里,并没有统一提交,没有写在磁盘上】
- exec -->commit 提交事务,执行multi到exec之间的代码,按顺序执行,如果其中一条出现运行时错误,则继续执行其他语句。
mysec_139.196.194.253:0>exec
"ERR EXEC without MULTI"
- exec 总是和multi成对出现的。
- 没有回滚机制:不同于mysql,如果出现异常,可以选择回滚代码,而不会真正写道磁盘上,而redis没有这个机制。
- watch -->另外一种的try catch,rollback机制
watch可以监听key的变化,当在事务期间,如果监听的key发生改变,则事务失效。相当于回滚到初始状态。
代码中使用

备注:语法错误和运行错误的区别
- 语法错误:set testKey --># 只有key没有value的语法错误,控制台是可以检测到的。
- 运行时错误: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可能遇到的问题:
- RedisTemplate increment 错误:ERR value is not an integer or out of range解决
出现位置:redisTemplate.opsForValue().increment(“testNum”);即不能自增,因为在数据库里,他是乱码的,不是数字类型,无法进行自增。需要序列化
这里贴下用的序列化方式:直接用就行了
- 关于RedisTemplate的ERR EXEC without MULTI错误
redisTemplate没有开启事务,需要在代码理指定:
redisTemplate.setEnableTransactionSupport(true);//开启事务
这段代码也可以写在上述序列化的时候。
-
关于事务中(指的是开启了事务,在multi和exce的之间)的查询会报空指针的问题
关闭事务的时候是没问题的,查资料也还没有好的解决方法,就先把查询语句去掉了,只留下了更新语句。希望有知道原理的朋友评论区指导一下。

















