前言:
- redis不僅僅是單純的緩存,它還有一些特殊的功能,在一些特殊場景上很好用。
- 本篇博文用來測試下使用redis來防止搶購商品超賣問題。
內容:
- 使用redis的list進行測試
思路是設置一個redis列表List,假設有十個商品,每次請求先判斷List的長度,小於十就能搶到商品,將用戶信息存放到List中。代碼如下
//進行搶購 protected function way_list(){ $num = $this->redis->lLen(); if($this->redis->lLen()>=self::AMOUNTLIMIT){ $this->writeLog("搶購失敗".$num); return; }else{ $this->redis->rPush($num); $this->writeLog("搶購成功".$num); } }
結果:失敗!
可以很明顯數量不對順序也不對。
分析了下原因,在代碼執行時,多用戶並發請求時,第一個用戶判斷List長度符合條件還未進行List寫入時,第二個用戶也通過了List長度判斷。所以就導致執行失敗。
這就沒有利用到redis的原子性
所以進行了改良
- 使用redis 的incrby。incrby將制定key 的值增加指定的增量,並返回增量后的值。是一個原子性操作。所謂的原子性操作就是執行該方法后要嘛成功要嘛失敗。
思路就是設置一個鍵值對存放被搶購數量,每次一個用戶進來就將該值加一進行判斷,如果小於搶購的商品數量則搶購成功,否則失敗。代碼如下
protected function way_string(){ //判斷是否有初始化 if(!$this->redis->exists(self::sold_name)){ $this->redis->setnx(self::sold_name,0); } if($this->redis->incrby(self::sold_name,1) > self::AMOUNTLIMIT){ $this->writeLog('失敗'); }else{ $this->writeLog('成功'); } }
結果
壓力測試了幾次都沒有出現問題
通過apache自帶的ab壓力測試,進行五百次連接請求,並發三百次,沒有出現超賣行為。
總結:會出現超賣主要是由於用戶在請求的時候,代碼在執行是有先后,會導致執行結果不符合預期。而采用redis的原子性就能避免。