事务
Redis 事务可以一次执行多个命令,有两个特性:
-
隔离性:事务的所有命令都会序列化、按顺序的执行,事务执行完后才会执行其他客服端的命令。
-
原子性: 事务中的命令要么全部被执行,要么全部不执行。
使用你事务时会遇到两个错误:
-
入队时出错,一般时因为语法错误引起的,加入事务队列就会报错,遇到这类错误,一般会放弃事务
-
EXEC调用后出错,列如对一个 值为
a1
的key
执行incr
,这类错误,即使某个命令产生了错误,其他命令依旧会继续执行执行,不会回滚
Reids 中的 WATCH
命令
使用 WATCH
命令可以监控键,如果被监控的键,再 EXEC
之前被修改,那么事务会放弃执行(注意:事务中的命令在 exec
命令后才开始执行)
EXEC
执行以后,无论事务是否执行成功,都会放弃对所有键的监控。
使用Java操控Redis事务命令
// 开启事务 Transaction transaction = jedis.multi(); // 提交事务 transaction.exec(); // 放弃事务 transaction.discard(); // 监控键 jedis.watch("balance", "debt"); // 放弃所有被监控的键 jedis.unwatch();
完整代码示例:
package com.project.test; import java.util.List; import org.junit.Before; import org.junit.Test; import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; /** * Rdis 事务 * */ public class TestTX { static final Jedis jedis = new Jedis("132.232.6.208", 6381); /** * 清空数据库 */ @Before public void flushdb() { jedis.flushDB(); } @Test public void commitTest() { // 开启事务 Transaction transaction = jedis.multi(); transaction.set("key-1", "value-1"); transaction.set("key-2", "value-2"); transaction.set("key-3", "value-3"); transaction.set("key-4", "value-4"); // 提交事务 transaction.exec(); System.out.println(jedis.keys("*")); } @Test public void discardTest() { // 开启事务 Transaction transaction = jedis.multi(); transaction.set("key-1", "value-1"); transaction.set("key-2", "value-2"); transaction.set("key-3", "value-3"); transaction.set("key-4", "value-4"); // 放弃事务 transaction.discard(); System.out.println(jedis.keys("*")); } /** * watch 命令会标记一个或多个键 * 如果事务中被标记的键,在提交事务之前被修改了,那么事务就会失败。 * @return * @throws InterruptedException */ @Test public void watchTest() throws InterruptedException { boolean resultValue = transMethod(10); System.out.println("交易结果(事务执行结果):" + resultValue); int balance = Integer.parseInt(jedis.get("balance")); int debt = Integer.parseInt(jedis.get("debt")); System.out.printf("balance: %d, debt: %d ", balance, debt); } // 支付操作 public static boolean transMethod(int amtToSubtract) throws InterruptedException { int balance; // 余额 int debt; // 负债 jedis.set("balance", "100"); jedis.set("debt", "0"); jedis.watch("balance", "debt"); balance = Integer.parseInt(jedis.get("balance")); // 余额不足 if (balance < amtToSubtract) { jedis.unwatch(); // 放弃所有被监控的键 System.out.println("Insufficient balance"); return false; } Transaction transaction = jedis.multi(); // 扣钱 transaction.decrBy("balance", amtToSubtract); Thread.sleep(5000); // 在外部修改 balance 或者 debt transaction.incrBy("debt", amtToSubtract); // list为空说明事务执行失败 List<Object> list = transaction.exec(); return !list.isEmpty(); } }