事務
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();
}
}
