1、Redis 的事务使用非常简单,也因为这种简单性,它的事务模型很不严格,这要求我们不能像使用关系数据库的事务一样来使用 Redis。
2、每个事务的操作都有begin、commit 和 rollback,Redis也不例外,也包括multi/exec/discard。multi 指示事务的开始,exec 指示事务的执行,discard 指示事务的丢弃,如下图所示:
- 3、原子性:指要么事物都成功,要么全部失败,但Redis的事务仅仅满足事务的个理性,隔离性肿的窜行化——当前的执行的事务有着不被其他事务打断的权利,如下图所示:
4、discard(丢弃):用于丢弃事务缓存队列中的所有指令,在 exec 执行之前
5、Watch:Redis在处理并发时,通常会使用分布式锁,但分布式锁是一种悲观锁,可以使用另外一种方式,Watch,该指令是一种乐观锁:
1)watch 会在事务开始之前盯住 1 个或多个关键变量,当事务执行时,也就是服务器收到了 exec 指令要顺序执行缓存的事务队列时,Redis 会检查关键变量自 watch 之后,是否被修改了 (包括当前事务所在的客户端)。如果关键变量被人动过了,exec 指令就会返回 null ,通知客户端事务执行失败,客户端可以选择重试等操作
2)Redis 禁止在 multi 和 exec 之间执行 watch 指令,而必须在 multi 之前做好盯住关键变量,否则会出错。
3)通过watch命令来实现余额加倍的实现:
import java.util.List; import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; public class TransactionDemo { public static void main(String[] args) { Jedis jedis = new Jedis(); String userId = "abc"; String key = keyFor(userId); jedis.setnx(key, String.valueOf(5)); # setnx 做初始化 System.out.println(doubleAccount(jedis, userId)); jedis.close(); } public static int doubleAccount(Jedis jedis, String userId) { String key = keyFor(userId); while (true) { jedis.watch(key); int value = Integer.parseInt(jedis.get(key)); value *= 2; // 加倍 Transaction tx = jedis.multi(); tx.set(key, String.valueOf(value)); List res = tx.exec(); if (res != null) { break; } } return Integer.parseInt(jedis.get(key)); // 重新获取余额 } public static String keyFor(String userId) { return String.format("account_%s", userId); } } |