1 概念和使用
熟悉关系型数据库的读者应该对事务比较了解,简单说,事务表示一组动作或者命令,要么全部执行完成,要么全部不执行。例如在社交网站上用户A关注了用户B,那么需要在用户A的关注表中加入用户B,并且在用户B的粉丝表中 添加用户A,这两个行为要么全部执行,要么全部不执行,否则会出现数据不一致的情况。
Redis提供了简单的事务功能,将一组需要执行的命令放在multi和exec两个命令之间。multi代表事务开始,exec代表事务结束,他们之间的命令是原子性执行的
127.0.0.1:6379> multi
OK
127.0.0.1:6379> sadd user:a:follow user:b
QUEUED
127.0.0.1:6379> sadd user:b:fans user:a
QUEUED
127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 1
上述操作可以看到sadd命令返回结果是QUEUE,代表命令没有真正执行,而是暂时保存在Redis中。如果在执行exec之前,在另外一个客户端执行sismember user:🅰️:follow user:b 返回的结果应该是0
如果想要停止事务的执行,可以使用discard命令代替exec
127.0.0.1:6379> multi
OK
127.0.0.1:6379> sadd user:a:follow user:c
QUEUED
127.0.0.1:6379> discard
OK
如果执行事务过程中出现问题,那么有两种处理机制
-
命令错误,整个事务无法进行
127.0.0.1:6379> multi OK 127.0.0.1:6379> set count 1 QUEUED 127.0.0.1:6379> incrr count 1 (error) ERR unknown command `incrr`, with args beginning with: `count`, `1`, 127.0.0.1:6379> exec (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379>
-
运行时错误, 例如在添加用户粉丝数时,误把sadd写成zadd,这种会出现运行时错误,因为语法时没错的
127.0.0.1:6379> multi OK 127.0.0.1:6379> sadd user:a:follow user:b QUEUED 127.0.0.1:6379> zadd user:b:fans 1 user:a QUEUED 127.0.0.1:6379> exec 1) (integer) 1 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 127.0.0.1:6379>
可以看到Redis并不支持回滚功能,sadd user:a:follow user:b命令已经执行成功,开发人员需要自己修复这类问题
有些应用需要在事务开始前保证key没有被其他客户端修改过,才执行事务,否则不执行,Redis提供了watch命令来实现这样的功能,如下
客户端1 | 客户端2 |
---|---|
set key java | |
watch key | |
multi | |
append key pytoon | |
append key jedis | |
exec | |
get key |
# 客户端 1
127.0.0.1:6379> set key java
OK
127.0.0.1:6379> watch key
OK
127.0.0.1:6379> multi
OK
127.0.0.1:6379> append key jedis
QUEUED
127.0.0.1:6379> exec
(nil)
127.0.0.1:6379> get key
"javapytoon"
127.0.0.1:6379>
# 客户端2
127.0.0.1:6379> append key pytoon
(integer) 10
可以看到客户端1在执行multi之前执行了watch key操作,客户端2在客户端1执行exec之前修改了key的value值,导致事务没有进行,返回nil
Jedis客户端操作
void transactionProccess(String key){
Jedis jedis = null;
try {
jedis = jedisSentinelPool.getResource();
// watch
jedis.watch(key);
// 开启事务
Transaction tx = jedis.multi();
// 处理
List<Object> response = tx.exec();
System.out.println("response: "+ response);
}finally {
if(jedis != null){
jedis.close();
}
}
}