1. 程式人生 > 實用技巧 >Redis之品鑑之旅(五)

Redis之品鑑之旅(五)

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();