Redis事务
Redis事务简介
Redis事务的本质是一组命令的执行,一个事务中的所有命令都会被序列化,所有命令按照入队的顺序执行,先入队的先执行;Redis事务没有隔离级别;单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。
一次性;顺序性;排他性
Redis事务执行顺序
- 开启事务(multi)
- 命令入队(多条命令)
- 执行事务(exec)
Redis事务相关命令
- watch key1 key2 … : 监视一或多个key,如果在事务执行之前,被监视的key被其他命令改动,则事务被打断 ( 类似乐观锁 )
- multi : 标记一个事务块的开始( queued )
- exec : 执行所有事务块的命令 ( 一旦执行exec后,之前加的监控锁都会被取消掉 )
- discard : 取消事务,放弃事务块中的所有命令
- 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>redis.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"));
}
}
}