一起聊聊redis(5)——c#的lua腳本應用實例之高併發搶口罩

本節內容

展示Redis數據庫在高併發下實現搶口罩的例子,文章分3個實現的模式

  1. 不加鎖
  2. 樂觀鎖
  3. Lua腳本

前言

寫於2020.2.8日疫情嚴重之日,搶口罩成為每晚8點檔黃金時間必備節目,在報道上看到太多不知真假的求助病重不能得到及時醫治的,有感於世事無常,珍愛身邊人.


不加鎖

在這個例子中程序運行是有bug的。當多人同時搶口罩的時候,可能會出現搶到的人數比口罩的庫存多的情況。先看下面實現的主要代碼

一起聊聊redis(5)——c#的lua腳本應用實例之高併發搶口罩

搶口罩的邏輯,這裡用到了哈希類型,使用這個類型主要是用它的自減方法。

一起聊聊redis(5)——c#的lua腳本應用實例之高併發搶口罩

初始化口罩庫存為3,Parallel實現併發搶口罩

一起聊聊redis(5)——c#的lua腳本應用實例之高併發搶口罩

運行結果

如上圖所示,會出現庫存超賣的情況,原因是這樣的。由於程序在判斷庫存和庫存減1兩個操作沒有實現原子性,雖然Redis是單線程運行的。但在某個客戶端的判斷庫存和庫存減1這兩個操作之間,會可能出現其他的客戶端的庫存減1的操作插在其中,造成以上的結果。

樂觀鎖

Redis中Watch命令是一個樂觀鎖,它可以在執行Exec之前,監視任意數量的數據庫鍵,當其中至少一個發生改變後,它會拒絕執行事務,如下圖所示。

一起聊聊redis(5)——c#的lua腳本應用實例之高併發搶口罩

在c#的StackExchange.Redis中沒有Watch命令,但它有事務的實現。

一起聊聊redis(5)——c#的lua腳本應用實例之高併發搶口罩

事務的條件用AddContidion方法添加,類似於Watch某個鍵值

運行結果,由於添加了鎖,所以不再存在超賣的情況。

Lua腳本

Lua腳本會在一個Redis服務端的偽客戶端執行代碼。天生自帶原子屬性,意味著lua的代碼都原子執行。另外使用Lua的腳本,可以代碼複用,以後其他語言編寫的客戶端也可以使用,這好處相當於存儲過程和數據庫的類比。

詳細的代碼和結果見下圖:

一起聊聊redis(5)——c#的lua腳本應用實例之高併發搶口罩

這裡為了演示方便,通常都是上傳腳本到服務端,讓服務端返回SHA1碼,下次執行直接傳SHA1碼

c#生成Sha1

在實際過程中,通常不會傳一大段lua腳本到服務端,就等於你不會傳一個存儲過程的邏輯到數據庫一個道理,根據上一節的內容,通常上傳完lua腳本到服務端會返回一個sha1的字符串,下次可以通過傳該字符串到服務端,服務端進行比對後找回對應的腳本執行。下面的代碼展示怎麼在c#中生成sha1


一起聊聊redis(5)——c#的lua腳本應用實例之高併發搶口罩

總結:

用Lua腳本是最好的個人覺得,因為它可以封裝好這一部分的邏輯,甚至這些邏輯改動了都不需重新編譯客戶端,只是修改腳本就好,當然腳本就不能在程序中硬編碼。


分享到:


相關文章: