秒殺系統(tǒng)

1.什么是秒殺系統(tǒng)?

秒殺系統(tǒng)是運(yùn)營活動(dòng)中的一個(gè)手段方式,其表現(xiàn)形式就是在一個(gè)特定的時(shí)間內(nèi)開放一個(gè)參與活動(dòng)的入口,參與用戶多而真正獲得活動(dòng)的機(jī)會(huì)少。最具有代表性質(zhì)的活動(dòng)之一就是搶購小米手機(jī),12306搶票。

2.秒殺系統(tǒng)的特點(diǎn)和遇到的問題

1.秒殺時(shí)有大量的用戶在同一時(shí)間內(nèi)進(jìn)入服務(wù),會(huì)有大量的請(qǐng)求到服務(wù)器。

帶來的問題:會(huì)導(dǎo)致大量請(qǐng)求直接到服務(wù)器中,可由于只有少量的用戶才能得到機(jī)會(huì)真正的走完后續(xù)的流程,所以需要限制大量的流量進(jìn)入服務(wù)后端。 --限流

2.大量的刷新商品詳情頁面等, 請(qǐng)求一些熱點(diǎn)的數(shù)據(jù)。

帶來的問題:不斷的請(qǐng)求會(huì)導(dǎo)致服務(wù)存儲(chǔ)層壓力過大。影響整個(gè)存儲(chǔ)系統(tǒng)的穩(wěn)定性。 --緩存,CND

3.請(qǐng)求的數(shù)量遠(yuǎn)遠(yuǎn)大于庫存的數(shù)量

帶來的問題:庫存問題 ,庫存必須精準(zhǔn),無誤的返回給用戶,不能出現(xiàn)超發(fā)和少發(fā)的情況。-異步處理,熱點(diǎn)分片。

2.秒殺系統(tǒng)遇到了哪些問題和一些處理方式。

秒殺場(chǎng)景大部分都是讀多寫少的情況,不過都是高并發(fā)下處理。處理的情況有三大特征 "快準(zhǔn)穩(wěn)"

快 在短時(shí)間內(nèi)必須給用戶搶購結(jié)果的一個(gè)反饋

準(zhǔn) 比如庫存 就10個(gè) 不能多發(fā)也不能少發(fā) ,最準(zhǔn)確的結(jié)果反饋給用戶

穩(wěn) 在大流量請(qǐng)求服務(wù)中,高性能服務(wù)是必須的也是活動(dòng)繼續(xù)的前提

3.秒殺系統(tǒng)庫存問題

其中最主要的是庫存的處理

1.首先我們第一層直接使用mysql進(jìn)行庫存處理
a.設(shè)置total庫存字段為unsigned

update product set total=total-1 where id=1 and total >0

如果多個(gè)用戶更新同一條記錄,最后提交會(huì)覆蓋前面的提交結(jié)果
這樣也會(huì)出現(xiàn)超發(fā)的情況
b.使用鎖機(jī)制
用悲觀鎖 先鎖住需要更新的商品信息
樂觀鎖

select version, totla from product where total >= num and id=1;
update product set total=total-num, version=version+1 where id=1 and version=version

悲觀鎖

select * from product where  id=1 for update;

使用mysql 去處理庫存, 用悲觀鎖 可以對(duì)單個(gè)商品的庫存被鎖定了,不過大量的請(qǐng)求去扣庫存的時(shí)候被一個(gè)請(qǐng)求鎖定了其他請(qǐng)求都處于等待的情況,這個(gè)時(shí)候 mysql的壓力非常大,最終數(shù)據(jù)庫的響應(yīng)變慢 ,樂觀鎖也會(huì)出現(xiàn)這種情況 ,這時(shí)候就出現(xiàn)了雪崩效應(yīng)。什么是雪崩效應(yīng),在我們計(jì)算機(jī)應(yīng)用系統(tǒng)里面因?yàn)槟骋慌_(tái)計(jì)算機(jī)產(chǎn)生錯(cuò)誤異常導(dǎo)致應(yīng)用系統(tǒng)服務(wù)異常,因而可能會(huì)讓應(yīng)用系統(tǒng)中的所有計(jì)算機(jī)崩潰,這種就是雪崩效應(yīng),非??植?,讓應(yīng)用系統(tǒng)徹底無法使用
當(dāng)數(shù)量為0時(shí) 就不需要處理用戶的請(qǐng)求 直接拋商品已秒殺完
2.第二層 使用緩存處理
a.使用redis隊(duì)列
規(guī)定一個(gè)的隊(duì)列長度,比如長度為10,超過這個(gè)長度不參與活動(dòng)沒直接返回活動(dòng)結(jié)束
使用先到先得的方法 比如庫存只有10個(gè) 只允許前100個(gè)請(qǐng)求進(jìn)入隊(duì)列里面 后面的請(qǐng)求都返回秒殺失敗,常駐進(jìn)程異步處理隊(duì)列里面前100個(gè)請(qǐng)求 里面的扣減對(duì)應(yīng)的庫存 ,庫存不足返回秒殺失敗
問題:整個(gè)請(qǐng)求的處理涉及很多服務(wù)調(diào)用也涉及很多其他的系統(tǒng),也會(huì)有部先到的請(qǐng)求由于后面的一些排隊(duì)的服務(wù)拖累,導(dǎo)致請(qǐng)求處理完成的時(shí)間反而比較后面的請(qǐng)求的情況。
而且是先到先得方法,還沒有運(yùn)氣的成分
b.把商品信息同步到緩存中,每個(gè)商品的已搶購商品數(shù)量用緩存計(jì)數(shù)器單獨(dú)計(jì)算起來

if ($redis_client->decrby($key, $parmas['num']) > -1) {
        //減庫
        //處理 數(shù)據(jù)庫減庫存    可以同步 或者 異步處理數(shù)據(jù)庫存
} else {
    //購買多個(gè)時(shí),如庫存不足,需要把數(shù)量加回去,否則會(huì)出現(xiàn)庫減庫存,商品并沒有賣出去
    $redis_client->incrby($key, $parmas['num']);
    return false;
}

4.秒殺系統(tǒng)其他問題
首選 如何限流 一般有三種方法,令桶法,漏桶算法,分布式鎖
令牌桶
算法描述
假如用戶請(qǐng)求的平均發(fā)送速率為r,則每隔1/r秒一個(gè)令牌被加入到桶中
假設(shè)桶中最多可以存放b個(gè)令牌。如果令牌到達(dá)時(shí)令牌桶已經(jīng)滿了,那么這個(gè)令牌會(huì)被丟棄
當(dāng)流量以速率v進(jìn)入,從桶中以速率v取令牌,拿到令牌的流量通過,拿不到令牌流量不通過,執(zhí)行熔斷邏輯
漏桶算法
漏桶算法思路很簡(jiǎn)單,水(請(qǐng)求)先進(jìn)入到漏桶里,漏桶以一定的速度出水,當(dāng)水流入速度過大會(huì)直接溢出,可以看出漏桶算法能強(qiáng)行限制數(shù)據(jù)的傳輸速率。
分布式鎖
https://img-blog.csdn.net/20180219144927282

image

限流

        $second = time();
        $flowKey = "flowlmt_${key}_${second}";
        $curQps = $redis->incr($flowKey)
        // 如果redis訪問異常,需要開放限制,防止阻塞后續(xù)業(yè)務(wù)
        if (false === $curQps) {
            $curQps = 0;
        }
        // 必須設(shè)置超時(shí)時(shí)間
        if ($curQps < 3) {
            $redis->expire($flowKey, 3);
        }

防刷控制

       $key = 'throt_' . $key;
        $ts = $redis->get($key);
        if (false === $ts) {
            return true;
        }
        $now = now() 毫秒
        if ($ts && $now - $ts < $ttl * 1000) {
            return false;
        }
        $redis->setex($key, $now, $ttl + 2);
        return true;

分布式鎖 用于用戶瞬間多次點(diǎn)擊搶購或者保證用戶信息被一個(gè)請(qǐng)求改變使用

   public function lock($key, $ttl = 1) {
        $result = $this->setnx($key, time() + $ttl);
        if ($result == 1) {
            return true;
        }
        $oldExpire = $this->get($key);
        if (false === $oldExpire || (int)$oldExpire > time()) {
            return false;
        }
        $currentExpire = $this->getset($key, time() + $ttl);
        if (false === $currentExpire || $oldExpire != $currentExpire) {
            return false;
        }
        return true;
    }

有效時(shí)間為什么都設(shè)置了>1s ,因?yàn)榕略谡?qǐng)求過程中 請(qǐng)求的處理大于1秒 ,其他請(qǐng)求就可以獲取到該鎖并改變前一個(gè)請(qǐng)求鎖住得部分。這樣就確保了只有一個(gè)請(qǐng)求獲取了該鎖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容