redis lua腳本redis事務(wù)實(shí)現(xiàn) 商品秒殺活動(dòng)案例

redis lua腳本redis事務(wù)實(shí)現(xiàn) 商品秒殺活動(dòng)案例

1. 前言

redis 利用單線程 IO多路復(fù)用 實(shí)現(xiàn)了 單命令操作的原子性,但是多個(gè)命令的操作就不具備原子性赴背。
不過可以利用redis 事務(wù) 或者 lua腳本 來實(shí)現(xiàn) 多命令操作的原子性哨免。
本文試圖通過模擬商品秒殺活動(dòng),演示怎么實(shí)現(xiàn)redis多命令操作具有原子性假栓。
用到的工具: spring boot ,redis template,lua腳本邪锌。

2 準(zhǔn)備工作

2.1 配置redis Template

參照 Spring Boot 整合 RedisCache,EhCache,GuavaCache實(shí)戰(zhàn)中reids配置方式芒炼。

2.2 定義接口

public interface GoodsService {
   /**
     * 通過lua腳本實(shí)現(xiàn)的秒殺
     * @param skuCode 商品編碼
     * @param buyNum 購(gòu)買數(shù)量
     * @return 購(gòu)買數(shù)量
     */
    Long flashSellByLuaScript(String skuCode,int buyNum);
    /**
     * 通過redis 事務(wù) 實(shí)現(xiàn)的秒殺
     * @param skuCode 商品編碼
     * @param buyNum 購(gòu)買數(shù)量
     * @return 購(gòu)買數(shù)量
     */
    Long flashSellByRedisWatch(String skuCode,int buyNum);
}

3. redis 事務(wù)方式

  1. redisTemplate.excute(SessionCallback sessionCallback) 是執(zhí)行事務(wù)的api
  2. 所以要實(shí)現(xiàn)SessionCallback 來實(shí)現(xiàn)redis 事務(wù)批狐。
  3. 如果直接 通過redisTemplate 執(zhí)行事務(wù)命令 會(huì)報(bào)redis.clients.jedis.exceptions.JedisDataException: Cannot use Jedis when in Multi. Please use Transation or reset jedis state
    異常。
public class GoodsServiceImpl implements GoodsService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
 @Override
    public Long flashSellByRedisWatch(String skuCode,int num){
    
        SessionCallback<Long> sessionCallback = new SessionCallback<Long>() {
            @Override
            public Long execute(RedisOperations operations) throws DataAccessException {
                int result = num;
                //redis 樂觀鎖
                operations.watch(skuCode);
                ValueOperations<String, String> valueOperations = operations.opsForValue();
                String goodsNumStr = valueOperations.get(skuCode);
                Integer goodsNum = Integer.valueOf(goodsNumStr);
                //標(biāo)記一個(gè)事務(wù)塊的開始鸳谜。
               //事務(wù)塊內(nèi)的多條命令會(huì)按照先后順序被放進(jìn)一個(gè)隊(duì)列當(dāng)中膝藕,
               //最后由 EXEC 命令原子性(atomic)地執(zhí)行。
                operations.multi();
                if (goodsNum >= num) {
                    valueOperations.increment(skuCode, 0 - num);
                } else {
                    result = 0;
                }
                //多條命令執(zhí)行的結(jié)果集合
                List exec = operations.exec();
                if(exec.size()>0){
                    System.out.println(exec);
                }
                return (long) result;
            }
        };
       return stringRedisTemplate.execute(sessionCallback);
    }
//省略 其他的方法
}

4. lua腳本方式

  1. 通過redis eval命令 執(zhí)行一個(gè)lua腳本
  2. 如果不明白 eval命令 閱讀 redis eval 命令詳解

4.1 編寫lua腳本

lua腳本是配置在 application.properties中的lua.flashSaleScript=

local buyNum = ARGV[1]
local goodsKey = KEYS[1]  
local goodsNum = redis.call('get',goodsKey) 
if goodsNum >= buyNum 
then redis.call('decrby',goodsKey,buyNum) 
return buyNum 
else 
return '0'
end

4.2 flashSellByLuaScript實(shí)現(xiàn)代碼

@Service
public class GoodsServiceImpl implements GoodsService {
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private LuaScript luaScript;
    @Override
    public Long flashSellByLuaScript(String skuCode,int num) {
        DefaultRedisScript<String> longDefaultRedisScript = new DefaultRedisScript<>(luaScript.flashSaleScript, String.class);
        String result = stringRedisTemplate.execute(longDefaultRedisScript, Collections.singletonList(skuCode),String.valueOf(num));
        return Long.valueOf(result);
    }
    //省略 其他的方法

5. linux ab 壓力測(cè)試

  1. 提前在redis 中 存儲(chǔ) 貨品編碼為 0001 的貨品 咐扭,庫(kù)存數(shù)量為 500.
  2. 通過 linux ab 壓力測(cè)試 進(jìn)行測(cè)試 芭挽。
  3. 分別對(duì)兩種實(shí)現(xiàn)方式 進(jìn)行各1000次的訪問,并發(fā)數(shù)為 100蝗肪。
  4. 每次請(qǐng)求只購(gòu)買一個(gè)商品

5.1 ab命令

  • shell
ab -n1000 -c100 -p data.json -T application/json http://192.168.33.222:8881/spring-boot/testFlashSell
  • data.json
{
'skuCode':'0001',
'num':1
}

5.2 redis 事務(wù)方式執(zhí)行后

  • 執(zhí)行一次 測(cè)試后袜爪,商品還剩余 369個(gè)。
  • 執(zhí)行第二次后薛闪,商品還剩余 237個(gè)辛馆。
  • 最后共執(zhí)行四次,就是共4000次請(qǐng)求后 商品數(shù)量才為0。
  • 這個(gè)跟redis watch 的樂觀鎖有關(guān)昙篙。因?yàn)椴皇敲看握?qǐng)求都能成功腊状。
  • 這種方式有可能會(huì)使本來可以賣完的商品 賣不完,或者需要更多的時(shí)間 才能售賣完苔可。
  • 好在 并沒有出現(xiàn)負(fù)數(shù)缴挖。
192.168.26.230:14>get 0001 
"0"

5.3 測(cè)試lua腳本方案

  • 執(zhí)行一次后,商品就已經(jīng)為0.同樣也沒有出現(xiàn)負(fù)數(shù)焚辅。

6.總結(jié)

我比較喜歡lua腳本的實(shí)現(xiàn)方式映屋。對(duì)于程序開發(fā)者而言, 學(xué)習(xí)lua腳本很簡(jiǎn)單同蜻,稍微看下就可以編寫用于redis 的lua腳本棚点。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市埃仪,隨后出現(xiàn)的幾起案子乙濒,更是在濱河造成了極大的恐慌陕赃,老刑警劉巖卵蛉,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異么库,居然都是意外死亡傻丝,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門诉儒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來葡缰,“玉大人,你說我怎么就攤上這事忱反》菏停” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵温算,是天一觀的道長(zhǎng)怜校。 經(jīng)常有香客問我,道長(zhǎng)注竿,這世上最難降的妖魔是什么茄茁? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮巩割,結(jié)果婚禮上裙顽,老公的妹妹穿的比我還像新娘。我一直安慰自己宣谈,他們只是感情好愈犹,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著闻丑,像睡著了一般甘萧。 火紅的嫁衣襯著肌膚如雪萝嘁。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天扬卷,我揣著相機(jī)與錄音牙言,去河邊找鬼。 笑死怪得,一個(gè)胖子當(dāng)著我的面吹牛咱枉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播徒恋,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼蚕断,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了入挣?” 一聲冷哼從身側(cè)響起亿乳,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎径筏,沒想到半個(gè)月后葛假,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡滋恬,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年聊训,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恢氯。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡带斑,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出勋拟,到底是詐尸還是另有隱情勋磕,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布敢靡,位于F島的核電站挂滓,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏醋安。R本人自食惡果不足惜杂彭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吓揪。 院中可真熱鬧亲怠,春花似錦、人聲如沸柠辞。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至习勤,卻和暖如春踪栋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背图毕。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工夷都, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人予颤。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓囤官,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蛤虐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子党饮,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容