事務
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\n", 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(); } }