本節內容
展示Redis數據庫在高併發下實現搶口罩的例子,文章分3個實現的模式
- 不加鎖
- 樂觀鎖
- Lua腳本
前言
寫於2020.2.8日疫情嚴重之日,搶口罩成為每晚8點檔黃金時間必備節目,在報道上看到太多不知真假的求助病重不能得到及時醫治的,有感於世事無常,珍愛身邊人.
不加鎖
在這個例子中程序運行是有bug的。當多人同時搶口罩的時候,可能會出現搶到的人數比口罩的庫存多的情況。先看下面實現的主要代碼
如上圖所示,會出現庫存超賣的情況,原因是這樣的。由於程序在判斷庫存和庫存減1兩個操作沒有實現原子性,雖然Redis是單線程運行的。但在某個客戶端的判斷庫存和庫存減1這兩個操作之間,會可能出現其他的客戶端的庫存減1的操作插在其中,造成以上的結果。
樂觀鎖
Redis中Watch命令是一個樂觀鎖,它可以在執行Exec之前,監視任意數量的數據庫鍵,當其中至少一個發生改變後,它會拒絕執行事務,如下圖所示。
在c#的StackExchange.Redis中沒有Watch命令,但它有事務的實現。
運行結果,由於添加了鎖,所以不再存在超賣的情況。
Lua腳本
Lua腳本會在一個Redis服務端的偽客戶端執行代碼。天生自帶原子屬性,意味著lua的代碼都原子執行。另外使用Lua的腳本,可以代碼複用,以後其他語言編寫的客戶端也可以使用,這好處相當於存儲過程和數據庫的類比。
詳細的代碼和結果見下圖:
c#生成Sha1
在實際過程中,通常不會傳一大段lua腳本到服務端,就等於你不會傳一個存儲過程的邏輯到數據庫一個道理,根據上一節的內容,通常上傳完lua腳本到服務端會返回一個sha1的字符串,下次可以通過傳該字符串到服務端,服務端進行比對後找回對應的腳本執行。下面的代碼展示怎麼在c#中生成sha1
總結:
用Lua腳本是最好的個人覺得,因為它可以封裝好這一部分的邏輯,甚至這些邏輯改動了都不需重新編譯客戶端,只是修改腳本就好,當然腳本就不能在程序中硬編碼。