藉助Redis做秒殺和限流的思考

最近羣裏聊起秒殺和限流,我自己沒有做過類似應用,但是工作中遇到過更大的數據和併發。

於是提出了一個簡單的模型:

var count = rds.inc(key);

if(count > 1000) throw "已搶光!"

藉助Redis單線程模型,它的inc是安全的,確保每次加一,然後返回加一後的結果。如果原來是234,加一了就是235,返回的一定是235,在此中間,不會有別的請求來打斷從而導致返回236或者其它。

其實我們可以理解爲inc的業務就是佔坑排隊,每人佔一個坑,拿到排隊小票後看看是不是超額了,再從業務層面輸出秒殺結果,甚至做一些更加複雜的業務。

六條提到限流,可能基於某種考慮,希望把key對應的count給限制在1000附近,可以接受1%偏差。

於是有了改進模型:

var count = rds.inc(key);

if(count > 1000){

    rds.dec(key);

    throw "超出限額!"

就加了一句,超出限額後,把小票給減回去^_^

 

採用Redis有一個好處,比如支持很多應用服務器一起搶……

當然,對於很大量的秒殺,這個模型也不一定合理,比如要槍10萬部手機,然後來了300萬用戶,瞬間擠上來。

這裏有個變通方法可以試一下,那就是準備10個Redis實例,每個放1萬。用戶請求過來的時候,可以隨機數或者散列取模,找對應實例來進行搶購。

同理可以直接更多用戶的場景。總的來說,在數據較大的時候,隨機和散列就具有一定統計學意義,相對來說是比較均衡的。

 

上面是大量秒殺的簡單場景,那麼小數據場景呢?比如就只有幾萬併發的場景

小數據場景,單應用實例,可以考慮把Redis都給省了。

初級模型:

Interlocked.Increase(ref count);

if(count >= 1000) throw "搶光啦!"

中級模型:

private volatile Int32 count;

var old = 0;

do {

    old = count;

    if(old >= 1000) throw "搶光啦!"

}while(Interlocked.CompareExchange(ref count, old + 1, old) != old);

這個CAS原子操作可是好東西,在x86指令集下有專門指令CMPXCHG來處理,在處理器級別確保比較和交換數據的原子性。大多數系統想要邁過10萬tps的門檻向100萬tps靠齊,就必須得實現無鎖操作lock-free,其中CAS是最爲簡單易懂,儘管有時候有ABA問題,但我們可以找到許多解決辦法。

 

在實際使用場景中,可能有更復雜的需求,那就另當別論,這裏只能班門弄斧幾個簡單易用的模型。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章