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

2021-09-07 16:30:39 字數 1326 閱讀 5120

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

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

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 while(interlocked.compareexchange(ref count, old + 1, old) != old);

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

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

秒殺和限流 redis

redis是單執行緒的,所以在redis中所有命令都是原子操作。而當要多條redis命令同時執行而不被打斷時,則需要使用redis的事務了。商品放到redis上面,每一次都在redis裡面執行操作,操作之前先watch key watch的作用就是檢測這個key,如果這key的事務被修改則不會執行,...

Redis事務和秒殺業務設計

redis事務是乙個單獨的隔離操作 事務中的所有命令都會序列化 按順序地執行。事務在執行的過程中,不會被其他客戶端傳送來的命令請求所打斷,redis事務的主要作用就是串聯多個命令防止別的命令插隊 multi 用於標記事務塊的開始。redis會將後續的命令逐個放入佇列中,然後才能使用exec命令原子化...

PHP 通過redis和mysql實現秒殺業務

db mysqldb getinstance info db fetchrow select from goods where goods id 1 判斷是否還有庫存 if info stock 0 減少庫存,num 只是乙個記錄修改資料的次數,可以判斷是否存在超賣現象 result db upda...