Redis 事务可以一次执行多个命令, 并且带有以下两个重要的保证:
- 事务是一个单独的隔离操作:事务中的所有命令都会序列化、按顺序地执行。事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
- 事务是一个原子操作:事务中的命令要么全部被执行,要么全部都不执行。但中间发生错误不会回滚,错误命令前后的其他命令都会被执行。
一个事务从开始到执行会经历以下三个阶段:
- 开始事务。
- 命令入队。
- 执行事务。
当一个client在一个连接中发出multi命令有,这个连接会进入一个事务上下文,该连接后续的命令并不是立即执行,而是先放到一个队列中。当从此连接受到exec命令后,redis会顺序的执行队列中的所有命令。并将所有命令的运行结果打包到一起返回给client.然后此连接就结束事务上下文。执行discard命令取消事务,放弃执行事务块内的所有命令,结束事务上下文。例子:
redis> multi
OK
redis> incr a
QUEUED
redis> incr b
QUEUED
redis> exec
1. (integer) 1
2. (integer) 1
watch命令
在执行命令的过程中,其他client如果修改这些(被watch的)值,那么exec就会执行失败,返回(nil)即此事务中所有命令执行失败。
例子:在图1watch和exec之间执行图2。
exec,discard,unwatch命令都会清除连接中的所有监视。
jedis例子:
@Test
public void jedisTransaction() throws Exception{
Jedis jedis = JedisUtils.getJedis();
String watch = jedis.watch("a","b");
Transaction multi = jedis.multi();
System.out.println(Thread.currentThread().getId()+"--"+watch);
//dosomething
multi.set("a","999");
multi.set("b","888");
try {
Thread.sleep(30000);
} catch (InterruptedException e) {
e.printStackTrace();
}
List<Object> exec = multi.exec();
System.out.println("---"+exec);
jedis.unwatch();
}
@Test
public void jedisTransactionAnotherClient() throws Exception{
Jedis jedis = JedisUtils.getJedis();
String watch = jedis.watch("a");
Transaction multi = jedis.multi();
System.out.println(Thread.currentThread().getId()+"--"+watch);
//dosometing
multi.set("a","777");
List<Object> exec = multi.exec();
System.out.println("---"+exec);
jedis.unwatch();
}