Redis之品鑑之旅(五)
阿新 • • 發佈:2020-08-24
Redis事務
- 原子性:就是最小的單位
- 一致性:好多命令,要麼全部執行成功,要麼全部執行失敗
- 隔離性:一個會話和另一個會話之間是互相隔離的
- 永續性:執行了就執行了,資料儲存在硬碟上
典型例子:銀行轉賬,A給B轉賬100萬,首先要A的賬戶減去100萬,然後B的賬戶增加100萬,如果中間斷了那就出問題了。所以我們人為的將這件事進行原子化,做成一個最小單位。要麼一起成功要麼一起失敗。
redis是一種簡單的資料儲存形式,redis的事務是不支援回滾的。
之前的sqlserver的事務,開了一個事務,去修改表的資料。然後有一個新的會話,或者新的連線,這個時候如果我們這兒連線去修改或者查詢這個表時,可能會一直等待,直到事務操作完畢。
而redis則不然,如果同樣的操作使用redis事務的話,會導致事務操作失敗。redis在開啟事務前監聽了3個要修改key的版本號,如果在這個事務期間,其他的連線可以修改這3個key對應的值,但是會影響當前開啟事務這個會話的操作,讓事務提交不成功。
//事務模式 using (RedisClient client = new RedisClient("127.0.0.1", 6379, "12345", 10)) { //刪除當前資料庫中的所有Key 預設刪除的是db0 client.FlushDb(); //刪除所有資料庫中的key //client.FlushAll(); client.Set("a", "1"); client.Set("b", "1"); client.Set("c", "1"); //獲取當前這三個key的版本號 實現事務 client.Watch("a", "b", "c"); using (var trans = client.CreateTransaction()) { trans.QueueCommand(p => p.Set("a", "3")); trans.QueueCommand(p => p.Set("b", "3")); trans.QueueCommand(p => p.Set("c", "3")); //提交事務 如果在觸發事務過程中,其他程序操作了當前的key,則事務提交失敗,就是沒有指令沒有修改成功 var flag = trans.Commit(); // ID KEY Console.WriteLine(flag); } //根據key取出值,返回string Console.WriteLine(client.Get<string>("a") + ":" + client.Get<string> ("b") + ":" + client.Get<string> ("c")); Console.ReadLine(); }
所以,在redis使用事務的情況下,必須使用watch進行監聽一下對應的key值,凡是需要事務操作的key,都要包含在watch裡面進行監聽。
單執行緒理解誤區:(看程式碼)
//單執行緒理解誤區 using (RedisClient client1 = new RedisClient("127.0.0.1", 6379, "12345", 10)) { //刪除當前資料庫中的所有Key 預設刪除的是db0 client1.FlushDb(); //刪除所有資料庫中的key //client.FlushAll(); client1.Del("number"); for (int i = 0; i < 10; i++) { Task.Run(() => { using (RedisClient client = new RedisClient("127.0.0.1", 6379, "12345", 10)) { if (client.Get("number") == null) { Console.WriteLine("number == null"); client.Set<string>("number", "100"); } else { Console.WriteLine("number != null"); } } }); } } Console.ReadLine();
在我的電腦上最終列印結果:
number == null
number == null
number == null
number == null
number != null
number != null
number != null
number != null
number != null
number != null
這裡面的語句(if (client.Get("number") == null))會出現4次執行成功,這會誤導我們對單執行緒的理解。說明進入這個判斷時,有4個同時進入了,這4個獲取過程會排隊到redis裡面,redis會一個個的進行執行,那麼這4個就都是成功的,所以會有4次成功執行,而這4次執行完之後,key就被賦值了,其他執行緒進入時,就會不為null。這裡面的單執行緒指的是redis執行的過程,而不是程式中執行緒執行的過程。如果想要只進入一次,就需要對這個過程加鎖。(我的理解是,因為我的CPU是2核4執行緒,所以會有4個執行緒同時進入)
//單執行緒理解誤區
using (RedisClient client1 = new RedisClient("127.0.0.1", 6379, "12345", 10))
{
//刪除當前資料庫中的所有Key 預設刪除的是db0
client1.FlushDb();
//刪除所有資料庫中的key
//client.FlushAll();
client1.Del("number");
for (int i = 0; i < 10; i++)
{
Task.Run(() =>
{
using (RedisClient client = new RedisClient("127.0.0.1", 6379, "12345", 10))
{
lock ("123")
{
if (client.Get("number") == null)
{
Console.WriteLine("number == null");
client.Set<string>("number", "100");
}
else
{
Console.WriteLine("number != null");
}
}
}
});
}
}
Console.ReadLine();