Redis事务
Redis事务简介
Redis事务的本质是一组命令的执行,一个事务中的所有命令都会被序列化,所有命令按照入队的顺序执行,先入队的先执行;Redis事务没有隔离级别;单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
一次性;顺序性;排他性
Redis事务执行顺序
1、开启事务(multi)
2、命令入队(多条命令)
3、执行事务(exec)
Redis事务相关命令
1、watch key1 key2 … : 监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 )
2、multi : 标记一个事务块的开始( queued )
3、exec : 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 )
4、discard : 取消事务,放弃事务块中的所有命令
5、unwatch : 取消watch对所有key的监控
事务正常执行时
127.0.0.1:6379> multi #开启事务 OK 127.0.0.1:6379> set k1 v1 #命令入队 QUEUED 127.0.0.1:6379> set k2 v2 QUEUED 127.0.0.1:6379> get k1 QUEUED 127.0.0.1:6379> exec #执行事务 1) OK 2) OK 3) "v1"
执行事务过程中取消事务
127.0.0.1:6379> multi OK 127.0.0.1:6379> LPUSH list 1 QUEUED 127.0.0.1:6379> LPUSH list 2 QUEUED 127.0.0.1:6379> LPUSH list 3 QUEUED 127.0.0.1:6379> LRANGE list 0 -1 QUEUED 127.0.0.1:6379> DISCARD OK 127.0.0.1:6379> exec (error) ERR EXEC without MULTI #事务取消后,multi指令也随之消失,命令无效 127.0.0.1:6379> LPOP list (nil)
当执行事务时出错时
1)当exec执行事务前出错
127.0.0.1:6379> multi OK 127.0.0.1:6379> set name kexing QUEUED 127.0.0.1:6379> SMEMBERS (error) ERR wrong number of arguments for 'smembers' command 127.0.0.1:6379> exec #exec前处出错时,无法提交事务,即事务被丢弃 (error) EXECABORT Transaction discarded because of previous errors.
2)当exec执行事务后出错
127.0.0.1:6379> multi OK 127.0.0.1:6379> set name kexing QUEUED 127.0.0.1:6379> SADD name 1 QUEUED 127.0.0.1:6379> exec #exec后出错,出错的命令失败,其他命令成功 1) OK 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 127.0.0.1:6379> get name "kexing"
watch监控,实现乐观锁
1)正常情况下:
127.0.0.1:6379> multi #开启事务 OK 127.0.0.1:6379> set money 1000 QUEUED 127.0.0.1:6379> set out 0 QUEUED 127.0.0.1:6379> decrby money 10 #余额-10 QUEUED 127.0.0.1:6379> incrby out 10 #花出+10 QUEUED 127.0.0.1:6379> exec #执行 1) OK 2) OK 3) (integer) 990 4) (integer) 10
2)使用watch监控money,当事务执行过程中money发生改变,提交事务会失败,提交后自动unwatch解锁
127.0.0.1:6379> get money "990" 127.0.0.1:6379> get out "10" 127.0.0.1:6379> watch money OK 127.0.0.1:6379> multi OK 127.0.0.1:6379> decrby money 10 QUEUED 127.0.0.1:6379> incrby money 10 QUEUED 127.0.0.1:6379> exec (nil) 127.0.0.1:6379> get money "1000"
在上面事务执行过程中(未提交),更改了money的值
127.0.0.1:6379> incrby money 10 (integer) 1000
watch检测到money值发生改变,因此不允许此次事务的执行,执行事务失败,当事务执行后,无论成功失败,都会自动unwatch解锁
Jedis操作事务
导入jedis、fastjson依赖
<dependencies> <!-- jedis --> <dependency> <groupId><a href="https://www.gaodaima.com/tag/redis" title="查看更多关于redis的文章" target="_blank">redis</a>.clients</groupId> <artifactId>jedis</artifactId> <version>3.3.0</version> </dependency> <!-- fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.73</version> </dependency> </dependencies>
public class RedisAffairs { public static void main(String[] args) { Jedis jedis = new Jedis("127.0.0.1",6379); JSONObject jsonObject = new JSONObject(); jsonObject.put("name","kexing"); jsonObject.put("age","20"); jsonObject.put("sex","0"); String user = jsonObject.toJSONString(); jedis.flushDB(); //开启事务 Transaction multi = jedis.multi(); //命令入队 try { multi.set("user1",user); multi.sadd("user1","1","2"); //运行时错误 multi.set("user2",user); multi.exec(); }catch (Exception e){ //命令错误,事务取消 multi.discard(); }finally { System.out.println(jedis.get("user1")); System.out.println(jedis.get("user2")); } } }