借助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問題,但我們可以找到許多解決辦法。

 

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM