最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

redis lua腳本實戰(zhàn)秒殺和減庫存的實現

 更新時間:2021年11月24日 12:03:22   作者:$碼出未來  
本文主要是學習一下redis lua腳本的編寫,以及在redisson這個redis客戶端中是怎樣使用的,實戰(zhàn)一下秒殺場景redis減庫存lua腳本的編寫,并偽真實環(huán)境壓測查看效果。感興趣的可以了解一下

前言

我們都知道redis是高性能高并發(fā)系統必不可少的kv中間件,它以高性能,高并發(fā)著稱,我們常常用它做緩存,將熱點數據或者是萬年不變的數據緩存到redis中,查詢的時候直接查詢redis,減輕db的壓力,分布式系統中我們也會拿它來做分布式鎖,分布式id,冪等來解決一些分布式問題,redis也支持lua腳本,而且能夠保證lua腳本執(zhí)行過程中原子性,這就使得它的應用場景很多,也很典型,在redisson這個redis客戶端中,它的各種分布式鎖底層就是使用lua來實現的。本文主要是學習一下redis lua腳本的編寫,以及在redisson這個redis客戶端中是怎樣使用的,實戰(zhàn)一下秒殺場景redis減庫存lua腳本的編寫,并偽真實環(huán)境壓測查看效果。

1.redisson介紹

redisson是一個redis的客戶端,它擁有豐富的功能,而且支持redis的各種模式,什么單機,集群,哨兵的都支持,各種各樣的分布式鎖實現,什么分布式重入鎖,紅鎖,讀寫鎖,信號量的,然后它操作redis的常用數據結構就跟操作jdk的各種集合一樣簡單。
這里我們稍微演示下,不做過多的介紹,api畢竟只是個api,有意思的都在它背后各種原理。

maven依賴

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.8.1</version>
</dependency>

創(chuàng)建RedissonClient對象

Config config = new Config();
config.useSingleServer()
        .setAddress("redis://xxx:xx");
RedissonClient redissonClient = Redisson.create(config);

它的功能超級多,下面只是列舉了一些常用的,還有什么Bloom過濾器,隊列等等。

// string
redissonClient.getBucket("name").set("zhangsan");
redissonClient.getBucket("name").get();

// hash
RMap<Object, Object> user = redissonClient.getMap("user");
user.put("name","zhangsan");
user.put("age",11);

// list
RList<String> names = redissonClient.getList("names");
names.add("zhangsan");
names.add("lisi");
names.add("wangwu");
// set
RSet<Object> nameSet = redissonClient.getSet("names");
nameSet.add("lisi");
nameSet.add("lisi");

//lock
RLock lock = redissonClient.getLock("lock");
lock.lock();
lock.unlock();

// 分布式id
RAtomicLong id = redissonClient.getAtomicLong("id");
long l = id.incrementAndGet();  

2. redis lua腳本編寫與執(zhí)行

其實redis中的lua腳本并不難,你也不需要把lua語言再去重學一遍,全憑感覺就好了,使用的時候去查下語法就ok了。
腳本中就一個redis.call() 應該算是函數吧(方法也可以),比如我要使用lua腳本實現set動作,就可以這樣寫

return redis.call('set','name','zhangsan');

其實就是跟redis交互命令一個樣子,再使用lua語言做一些條件分支,循環(huán)啥的,就完成了一些稍微復雜的邏輯。
下面自己寫一遍扣減庫存的邏輯,你就會這個玩意了。
上面是介紹了lua腳本的編寫,下面我們介紹下這個執(zhí)行。
不管是redis自己帶的那個客戶端,還是jedis,jediscluster,redistemplate,redisson這些客戶端都是支持lua腳本api的,其實就是eval,evalsha,scriptload 這幾個命令用的比較頻繁,我這里把菜鳥教程上面關于介紹redis腳本命令截圖過來

在這里插入圖片描述

多說無益,這里直接使用redisson客戶端實踐一下。

public class RedisLua {
    private static  final Config config ;
    private static  final RedissonClient redisson ;
    static {
        config = new Config();
        config.useSingleServer()
                .setAddress("redis://ip:port");

        redisson =  Redisson.create(config);
    }
    public static void main(String[] args) throws InterruptedException {
        redisson.getBucket("name").set(11);
        RScript script = redisson.getScript();
        String result = script.eval(RScript.Mode.READ_ONLY, new StringCodec(),"return redis.call('get','name');", RScript.ReturnType.VALUE);
        System.out.println(result);
    }
}

可以看到就是使用getScript方法獲取一個script對象,然后調用script 對象的eval方法,這個script對象其實還是用好多個方法的,evalSha等等,可以自己研究下。然后就是通過lua腳本獲取我上面set進去的name值。
我們在看上面菜鳥教程對腳本命令的介紹的時候,還發(fā)現有key… arg…這些東西,這個我認為就是動態(tài)替換(傳參)的。
比如我現在不獲取name這個key的值了,我要獲取age 的,或者是我要直接set一個值,這個時候我就可以lua腳本中有兩個變量與你傳的參數對等起來
KEYS[1] ARGV[1]
你可以看作是數組,不過它的位置是從1開始的。

RScript script = redisson.getScript();
List<Object> keys=new ArrayList<>();
keys.add("age");
String re = script.eval(
        RScript.Mode.READ_WRITE,
        new StringCodec(),
        "return redis.call('set',KEYS[1],ARGV[1]);",
        RScript.ReturnType.VALUE,
        keys,1);
Object age = redisson.getBucket("age").get();
System.out.println(age);

KEYS[1] 就對應這age, ARGV[1]就對應1,同理,KEYS[2] 就對應keys集合中的第二個元素。

3.redis減庫存lua腳本

先介紹下下單減庫存是怎么干的吧,其實一般庫存有可用庫存與預占庫存,再下單的時候,就將可用庫存減去你購買的商品數量看看是否是小于0,如果是小于0的話,說明庫存不夠了,就不讓下單購買了,如果可用庫存充足,可用庫存減去購買商品數量,預占庫存加上你購買商品數量,當用戶超時未支付或者是手動取消訂單的時候,就會去預占庫減去用戶購買商品數,可用庫存加上商品數,其實還有一個已售庫存,商家發(fā)貨,已售庫存加上商品數,預占減去商品數,大體上是這個邏輯。
現在可以想下,如果讓你來實現下單預占庫存的功能,你會怎么做,數據庫三個庫存字段這個不用說了。
首先你得先把可售庫存查詢出來,然后與購買商品數量進行比較,如果是可售庫存大于這個購買商品數量,就可以購買,更新可售庫存與預占庫存。
如果上面這段代碼邏輯你不加一些特殊手段的處理,那ok,高并發(fā)場景下絕對會出現超賣現象。
如果是秒殺場景呢?血虧。
這個時候我們可能會增加一些特殊手段來解決,比如說加鎖,加分布式鎖,將這一段的業(yè)務邏輯鎖住,這個時候就不會出現那種超賣現象了, 但是這個中情況如果售罄的話,也會一直查詢數據庫。秒殺的時候,流量那么高,你不能讓這么大的流量直接查庫,如果商品售罄直接返回就可以了,不用再查詢數據庫了。
一般秒殺的時候,會將商品的庫存同步推到redis中,流量過來的時候,會先扣減redis的庫存,如果redis成功了,才扣減數據庫中的庫存,如果redis中的庫存沒了,直接返回就ok,這樣大流量就不會直接沖擊數據庫了,那么redis要實現這段邏輯的話,就需要lua腳本的原子性了。
接下來我們就實現一下lua腳本扣減庫存邏輯。

public static final String LOCK_STOCK_LUA=  "local counter = redis.call('hget',KEYS[1],ARGV[1]); \n" +
                                                "local result  = counter - ARGV[2];" +
                                                "if(result>=0 ) then \n" +
                                                "   redis.call('hset',KEYS[1],ARGV[1],result);\n" +
                                                "   redis.call('hincrby',KEYS[1],ARGV[3],ARGV[2]);\n" +
                                                "   return 1;\n" +
                                                "end;\n" +
                                                "return 0;\n";

我這里已經寫好了,直接貼出來。
數據設計大體是這個樣子的,使用hash數據結構
商品:{
“可售庫存”:100,
“預占庫存”:0,
“已售庫存”:0
}

local counter = redis.call('hget',KEYS[1],ARGV[1]);

獲取可售庫存數量

local result  = counter - ARGV[2];
if(result>=0 ) then

可售庫存減去要購買的商品數量,如果是大于0的話,說明庫存還夠。

redis.call('hset',KEYS[1],ARGV[1],result);
redis.call('hincrby',KEYS[1],ARGV[3],ARGV[2]);
return 1;

重新設置可售庫存數量,增加預占庫存,然后返回1
如果是庫存不夠的話,直接返回0了就。

4.實戰(zhàn)

4.1 減庫存邏輯

減庫存邏輯其實就是先是用lua腳本減redis庫存,如果成功再去減數據庫中的真實庫存,如果減redis庫存失敗,庫存不足,就不會再走后面減真實庫存的邏輯了。
這塊的話,我是寫了一個庫存服務,實現了這段邏輯,但是總感覺有各種數據不一致的問題,當然不是超賣,而是少賣問題,這里就不發(fā)出來了。

4.2 壓測

我們這個實戰(zhàn)是在阿里云進行的
redis選的是容器服務,按秒計費,配置是0.5c1g
mysql也是選擇的容器服務,配置是0.5c1g
庫存服務是云服務器,按小時計費的那種,配置是2c4g,因為要部署多個服務跟實例,選擇的比較大。
壓測也是使用的阿里云的性能測試服務。

在這里插入圖片描述

在這里插入圖片描述

redis監(jiān)控,可以看到,這點并發(fā)對redis來說就是毛毛雨。cpu才使用7%

在這里插入圖片描述

云服務器這塊手速有點慢,沒截圖出來,cpu跟內存都在50%左右。

在這里插入圖片描述

mysql數據庫,可以看到cpu飆上去了,內存飆上去了。

在這里插入圖片描述

數據庫數據:

在這里插入圖片描述

可以發(fā)現并沒有出現超賣現象。

到此這篇關于redis lua腳本實戰(zhàn)秒殺扣減庫存的實現的文章就介紹到這了,更多相關redis lua戰(zhàn)秒殺扣減庫存內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Redis中序列化的兩種實現

    Redis中序列化的兩種實現

    本文主要介紹了Redis中序列化的兩種實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-07-07
  • 淺談Redis中的內存淘汰策略和過期鍵刪除策略

    淺談Redis中的內存淘汰策略和過期鍵刪除策略

    本文主要介紹了淺談Redis中的內存淘汰策略和過期鍵刪除策略,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Redis3.2.6配置文件詳細中文說明

    Redis3.2.6配置文件詳細中文說明

    本文為大家分享了Redis3.2.6配置文件詳細中文說明,非常詳細收藏起來以后工作有用
    2018-10-10
  • React?組件的常用生命周期函數匯總

    React?組件的常用生命周期函數匯總

    這篇文章主要介紹了React?組件的常用生命周期函數匯總,組件的生命周期有助于理解組件的運行方式、完成更復雜的組件功能、分析組件錯誤原因等
    2022-08-08
  • Redis sentinel節(jié)點如何修改密碼

    Redis sentinel節(jié)點如何修改密碼

    這篇文章主要介紹了Redis sentinel節(jié)點如何修改密碼問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • SpringBoot集成Redis的思路詳解

    SpringBoot集成Redis的思路詳解

    Redis是一個開源的使用ANSI C語言編寫、支持網絡、可基于內存亦可持久化的日志型、Key-Value數據庫,并提供多種語言的API。接下來通過本文給大家分享SpringBoot集成Redis的詳細過程,感興趣的朋友一起看看吧
    2021-10-10
  • Redis如何實現刷票過濾

    Redis如何實現刷票過濾

    這篇文章主要介紹了Redis如何實現刷票過濾問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • Redis對象與redisObject超詳細分析源碼層

    Redis對象與redisObject超詳細分析源碼層

    這篇文章主要介紹了Redis對象與redisObject源碼層的分析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧
    2022-11-11
  • redis在php中常用的語法【推薦】

    redis在php中常用的語法【推薦】

    string是redis最基本的類型,而且string類型是二進制安全的。這篇文章主要介紹了redis在php中常用的語法,需要的朋友可以參考下
    2018-08-08
  • redis樂觀鎖與悲觀鎖的實戰(zhàn)?

    redis樂觀鎖與悲觀鎖的實戰(zhàn)?

    Redis提供了兩種鎖機制,即樂觀鎖和悲觀鎖。本文主要介紹了redis樂觀鎖與悲觀鎖的實戰(zhàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2023-04-04

最新評論

南通市| 绥滨县| 铅山县| 尼勒克县| 樟树市| 安徽省| 上虞市| 岢岚县| 保靖县| 常德市| 丰城市| 通许县| 卫辉市| 新野县| 白沙| 洞口县| 越西县| 阳朔县| 长汀县| 云梦县| 金堂县| 电白县| 北宁市| 高淳县| 监利县| 武夷山市| 宜兰县| 永靖县| 获嘉县| 德江县| 正阳县| 东方市| 巩义市| 城市| 辉南县| 邹城市| 温泉县| 湛江市| 上思县| 彭阳县| 昌乐县|