redis使用Lua腳本解決多線程下的超賣問題及原因解析
一.多線程下引起的超賣問題呈現(xiàn)
1.1.我先初始化庫存數(shù)量為1、訂單數(shù)量為0


1.2.開啟3個線程去執(zhí)行業(yè)務
業(yè)務為:判斷如果說庫存數(shù)量大于0,則庫存減1,訂單數(shù)量加1
結果為:庫存為-2,訂單數(shù)量為3
原因:如下圖所示,這是因為分別有6個指令(3個庫存減1指令,3個訂單數(shù)量加1指令)在redis服務端執(zhí)行導致的。
namespace MengLin.Shopping.Redis.LuaScript
{
public class SecKillOriginal
{
static SecKillOriginal()
{
using (RedisClient client = new RedisClient("127.0.0.1", 6379))
{
//刪除當前數(shù)據(jù)庫中的所有Key, 默認刪除的是db0
client.FlushDb();
//刪除所有數(shù)據(jù)庫中的key
client.FlushAll();
//初始化庫存數(shù)量為1和訂單數(shù)量為0
client.Set("inventoryNum", 1);
client.Set("orderNum", 0);
}
}
public static void Show()
{
for (int i = 0; i < 3; i++)
{
Task.Run(() =>
{
using (RedisClient client = new RedisClient("127.0.0.1", 6379))
{
int inventoryNum = client.Get<int>("inventoryNum");
//如果庫存數(shù)量大于0
if (inventoryNum > 0)
{
//給庫存數(shù)量-1
var inventoryNum2 = client.Decr("inventoryNum");
Console.WriteLine($"給庫存數(shù)量-1后的數(shù)量-inventoryNum: {inventoryNum2}");
//給訂單數(shù)量+1
var orderNum = client.Incr("orderNum");
Console.WriteLine($"給訂單數(shù)量+1后的數(shù)量-orderNum: {orderNum}");
}
else
{
Console.WriteLine($"搶購失敗: 原因是因為沒有庫存");
}
}
});
}
}
}
}

二.使用Lua腳本解決多線程下超賣的問題以及為什么
2.1.修改后的代碼如下
結果為:如下圖所示,庫存為0、訂單數(shù)量為1,并沒有出現(xiàn)超賣的問題且有2個線程搶不到。
namespace MengLin.Shopping.Redis.LuaScript
{
public class SecKillLua
{
/// <summary>
/// 使用Lua腳本解決多線程下變賣的問題
/// </summary>
static SecKillLua()
{
using (RedisClient client = new RedisClient("127.0.0.1", 6379))
{
//刪除當前數(shù)據(jù)庫中的所有Key, 默認刪除的是db0
client.FlushDb();
//刪除所有數(shù)據(jù)庫中的key
client.FlushAll();
//初始化庫存數(shù)量為1和訂單數(shù)量為0
client.Set("inventoryNum", 1);
client.Set("orderNum", 0);
}
}
public static void Show()
{
for (int i = 0; i < 3; i++)
{
Task.Run(() =>
{
using (RedisClient client = new RedisClient("127.0.0.1", 6379))
{
//如果庫存數(shù)量大于0,則給庫存數(shù)量-1,給訂單數(shù)量+1
var lua = @"local count = redis.call('get',KEYS[1])
if(tonumber(count)>0)
then
--return count
redis.call('INCR',ARGV[1])
return redis.call('DECR',KEYS[1])
else
return -99
end";
Console.WriteLine(client.ExecLuaAsString(lua, keys: new[] { "inventoryNum" }, args: new[] { "orderNum" }));
}
});
}
}
}
}


三.為什么使用Lua腳本就能解決多線程下的超賣問題呢?
是因為Lua腳本把3個指令,分別是:判斷庫存數(shù)量是否大于0、庫存減1、訂單數(shù)量加1,這3個指令打包放在一起執(zhí)行了且不能分割,相當于組裝成了原子指令,所以避免了超賣問題。
在redis中我們盡量使用原子指令從而避免一些并發(fā)的問題。

到此這篇關于redis使用Lua腳本解決多線程下的超賣問題以及為什么的文章就介紹到這了,更多相關redis多線程超賣內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Govern Service 基于 Redis 的服務治理平臺安裝過程詳解
Govern Service 是一個輕量級、低成本的服務注冊、服務發(fā)現(xiàn)、 配置服務 SDK,通過使用現(xiàn)有基礎設施中的 Redis 不用給運維部署帶來額外的成本與負擔,接下來通過本文給大家分享Govern Service 基于 Redis 的服務治理平臺的相關知識,感興趣的朋友一起看看吧2021-05-05
redis-cli登錄遠程redis服務并批量導入數(shù)據(jù)
本文主要介紹了redis-cli登錄遠程redis服務并批量導入數(shù)據(jù),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2023-10-10

