首先要了解的是:1.Redis如何保證原子性?
答案很簡單,因爲redis是單線程。
一、看下面代碼(僅僅是看,後面貼的有代碼可複製)
分析:通過jmeter併發測試發現仍然出現類似超賣情況,
上圖中 llen 和 lpush 兩次操作如果單獨執行是具備原子性的,但是這兩個操作組合起來纔算是完成一個業務,那麼這2個命令組合起來就不具備原子性,所有在兩個操作之間其他客戶端會出現髒讀。
二、再優化代碼如下:
在後臺設置紅包的時候執行以下操作,
然後用戶領取紅包請求接口的時候只做一步redis操作。
public function getPacket(Request $request) {
$packet_id = $request->input('packet_id',0);
if(!$packet_id){
return Response::error('缺少參數:packet_id') ;
}
$redisConfig = config('database.redis.default');
$redis = new Client($redisConfig);
$count = $redis->lpop('red_packet_id:'.$packet_id);
if(!$count){
return Response::error('已經搶光了哦') ;
}
return Response::success('恭喜您,搶到了哦!') ;
}
總結:
這裏利用 redis 操作的原子性來實現。首先我們把 紅包個數或者庫存 存在“red_packet_id:1”這個列表中,假設id=1的這個紅包的可領取個數爲10個,就往列表中push10個數,這個數沒有實際意義,僅僅只是代表紅包的可被領取次數。搶購或者紅包到開始領取時間後,每到來一個用戶,就從“red_packet_id:1”中 pop 一個數,表示用戶搶購成功。當列表爲空時,表示已經被搶光了。因爲列表的pop操作是原子的,即使有很多用戶同時到達,也是依次執行的。