一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

Mysql|Sql Server|Oracle|Redis|MongoDB|PostgreSQL|Sqlite|DB2|mariadb|Access|數(shù)據(jù)庫技術(shù)|

服務(wù)器之家 - 數(shù)據(jù)庫 - Redis - redis lua腳本實戰(zhàn)秒殺和減庫存的實現(xiàn)

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

2022-01-19 18:13$碼出未來 Redis

本文主要是學(xué)習(xí)一下redis lua腳本的編寫,以及在redisson這個redis客戶端中是怎樣使用的,實戰(zhàn)一下秒殺場景redis減庫存lua腳本的編寫,并偽真實環(huán)境壓測查看效果。感興趣的可以了解一下

前言

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

1.redisson介紹

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

maven依賴

?
1
2
3
4
5
<dependency>
    <groupid>org.redisson</groupid>
    <artifactid>redisson</artifactid>
    <version>3.8.1</version>
</dependency>

創(chuàng)建redissonclient對象

?
1
2
3
4
config config = new config();
config.usesingleserver()
        .setaddress("redis://xxx:xx");
redissonclient redissonclient = redisson.create(config);

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 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語言再去重學(xué)一遍,全憑感覺就好了,使用的時候去查下語法就ok了。
腳本中就一個redis.call() 應(yīng)該算是函數(shù)吧(方法也可以),比如我要使用lua腳本實現(xiàn)set動作,就可以這樣寫

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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對象,然后調(diào)用script 對象的eval方法,這個script對象其實還是用好多個方法的,evalsha等等,可以自己研究下。然后就是通過lua腳本獲取我上面set進(jìn)去的name值。
我們在看上面菜鳥教程對腳本命令的介紹的時候,還發(fā)現(xiàn)有key… arg…這些東西,這個我認(rèn)為就是動態(tài)替換(傳參)的。
比如我現(xiàn)在不獲取name這個key的值了,我要獲取age 的,或者是我要直接set一個值,這個時候我就可以lua腳本中有兩個變量與你傳的參數(shù)對等起來
keys[1] argv[1]
你可以看作是數(shù)組,不過它的位置是從1開始的。

?
1
2
3
4
5
6
7
8
9
10
11
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] 就對應(yīng)這age, argv[1]就對應(yīng)1,同理,keys[2] 就對應(yīng)keys集合中的第二個元素。

3.redis減庫存lua腳本

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

?
1
2
3
4
5
6
7
8
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";

我這里已經(jīng)寫好了,直接貼出來。
數(shù)據(jù)設(shè)計大體是這個樣子的,使用hash數(shù)據(jù)結(jié)構(gòu)
商品:{
“可售庫存”:100,
“預(yù)占庫存”:0,
“已售庫存”:0
}

?
1
local counter = redis.call('hget',keys[1],argv[1]);

獲取可售庫存數(shù)量

?
1
2
local result  = counter - argv[2];
if(result>=0 ) then

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

?
1
2
3
redis.call('hset',keys[1],argv[1],result);
redis.call('hincrby',keys[1],argv[3],argv[2]);
return 1;

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

4.實戰(zhàn)

4.1 減庫存邏輯

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

4.2 壓測

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

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

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

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

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

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

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

mysql數(shù)據(jù)庫,可以看到cpu飆上去了,內(nèi)存飆上去了。

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

數(shù)據(jù)庫數(shù)據(jù):

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

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

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

原文鏈接:https://blog.csdn.net/yuanshangshenghuo/article/details/115831912

延伸 · 閱讀

精彩推薦
  • Redis詳解Redis復(fù)制原理

    詳解Redis復(fù)制原理

    與大多數(shù)db一樣,Redis也提供了復(fù)制機(jī)制,以滿足故障恢復(fù)和負(fù)載均衡等需求。復(fù)制也是Redis高可用的基礎(chǔ),哨兵和集群都是建立在復(fù)制基礎(chǔ)上實現(xiàn)高可用的...

    李留廣10222021-08-09
  • Redisredis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    redis中如何使用lua腳本讓你的靈活性提高5個逼格詳解

    這篇文章主要給大家介紹了關(guān)于redis中如何使用lua腳本讓你的靈活性提高5個逼格的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具...

    一線碼農(nóng)5812019-11-18
  • RedisRedis如何實現(xiàn)數(shù)據(jù)庫讀寫分離詳解

    Redis如何實現(xiàn)數(shù)據(jù)庫讀寫分離詳解

    Redis的主從架構(gòu),能幫助我們實現(xiàn)讀多,寫少的情況,下面這篇文章主要給大家介紹了關(guān)于Redis如何實現(xiàn)數(shù)據(jù)庫讀寫分離的相關(guān)資料,文中通過示例代碼介紹...

    羅兵漂流記6092019-11-11
  • RedisRedis 事務(wù)知識點相關(guān)總結(jié)

    Redis 事務(wù)知識點相關(guān)總結(jié)

    這篇文章主要介紹了Redis 事務(wù)相關(guān)總結(jié),幫助大家更好的理解和學(xué)習(xí)使用Redis,感興趣的朋友可以了解下...

    AsiaYe8232021-07-28
  • Redisredis實現(xiàn)排行榜功能

    redis實現(xiàn)排行榜功能

    排行榜在很多地方都能使用到,redis的zset可以很方便地用來實現(xiàn)排行榜功能,本文就來簡單的介紹一下如何使用,具有一定的參考價值,感興趣的小伙伴們...

    乘月歸5022021-08-05
  • Redisredis 交集、并集、差集的具體使用

    redis 交集、并集、差集的具體使用

    這篇文章主要介紹了redis 交集、并集、差集的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友...

    xiaojin21cen10152021-07-27
  • RedisRedis全量復(fù)制與部分復(fù)制示例詳解

    Redis全量復(fù)制與部分復(fù)制示例詳解

    這篇文章主要給大家介紹了關(guān)于Redis全量復(fù)制與部分復(fù)制的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家學(xué)習(xí)或者使用Redis爬蟲具有一定的參考學(xué)習(xí)...

    豆子先生5052019-11-27
  • RedisRedis的配置、啟動、操作和關(guān)閉方法

    Redis的配置、啟動、操作和關(guān)閉方法

    今天小編就為大家分享一篇Redis的配置、啟動、操作和關(guān)閉方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    大道化簡5312019-11-14
主站蜘蛛池模板: 窝窝午夜理伦影院 | 国产激情一区二区三区四区 | 精品综合久久久久久88小说 | 亚洲国产情侣一区二区三区 | 免费成年视频 | 久久99re2热在线播放7 | 国产高清在线看 | 4虎影院在线观看 | 国内精品91东航翘臀女神在线 | www.91在线视频 | 国产一区二区三区丶四区 | 大妹子最新视频在线观看 | 欧美又黄又激烈真实床戏 | 日韩免费在线看 | 特黄aa级毛片免费视频播放 | 999久久免费高清热精品 | 色色色色色色网 | 国产精品亚洲午夜一区二区三区 | 男女男精品视频免费观看 | 久久免费国产 | 国产精品区一区二区免费 | 晓雪老师我要进你里面好爽 | 久久日本片精品AAAAA国产 | 亚洲人成网站在线观看妞妞网 | 亚洲毛片基地4455ww | 日韩aⅴ在线观看 | 亚洲羞羞裸色私人影院 | 岛国最新资源网站 | 精品久久久久久无码人妻国产馆 | 亚洲免费在线观看 | 吉泽明步高清无码中文 | 欧美视频一 | 午夜国产在线视频 | 四虎comwww最新地址 | 女仆色网址 | ady@ady9.映画网| 国产1区2区在线观看 | 韩国情事伦理片观看地址 | 欧美免赞性视频 | 无人区免费一二三四乱码 | 日本阿v精品视频在线观看 日本xxx片免费高清在线 |