laravel redis秒殺功能
阿新 • • 發佈:2022-05-19
1.初始化:
秒殺商品,將商品以list資料型別存入redis(每個數量為一個元素);
2.購買:
1)購買使用者入佇列,如果使用者佇列長度超過指定的排隊長度,則返回排隊數過多
2)如果使用者佇列長度小於指定的排隊長度,然後生成訂單,減去庫存。下單完成
<?php namespace App\Http\Controllers; use App\Models\Goods; use App\Models\Orders; use App\Services\RedisLock; use Exception; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Redis; class MiaoshaController extends Controller { //排隊人數 private $listNumber = 50; /** * 實現秒殺 */ public function secondsKill(Request $request) { try { $user_data = $request->only(['user_id', 'goods_id']); if ( !$user_data ) { return $this->failed('資料不能為空'); } $goodsId = $user_data['goods_id']; $userId = $user_data['user_id']; #訪問使用者入隊介面 $user_list = $this->requestUser($userId); if ( !$user_list ) { return $this->failed('排隊數大於商品總數:'.Redis::llen('user_list')); } #消費商品,從佇列中取出商品 $count = Redis::lpop('goods_store:' . $goodsId); if(!$count) { return $this->failed('商品搶光了'); } #進入redis鎖 $redisLock = new RedisLock($userId); $lock = $redisLock->lock(); if ($lock) { #最後進入資料庫操作(每次固定消費1個) $mysql_data = $this->storeOrder($userId, $count, '1'); if ( !$mysql_data ) { $redisLock->unlock(); return $this->failed('生成訂單失敗'); } else { #關閉鎖 $redisLock->unlock(); return $this->success('搶購成功'); } } return $this->success('生成訂單失敗'); } catch (Exception $e) { throw $e; } } /** * 將商品加入redis */ public function storageGoods(Request $request) { try { #查詢商品 $resutl = Goods::where('id', $request->goods_id)->select(['store','id'])->first(); $store = $resutl->store; $res = Redis::llen('goods_store:' . $resutl->id); $count = $store - $res; for($i = 0;$i < $count; $i++){ Redis::lpush('goods_store:' . $resutl->id, $resutl->id); } return $this->success('加入成功'.$count); } catch (Exception $e) { throw $e; } } /** * 將使用者也存入佇列中(就是將訪問請求資料)(此處沒有進行使用者過濾,同一個使用者進行多次請求也會進入佇列) */ private function requestUser($userId) { $res = Redis::llen('user_list'); #判斷排隊數 if ($res = Redis::llen('user_list') > $this->listNumber) { // return '排隊數大於商品總數'; return false; } #新增資料 Redis::lpush('user_list', $userId); return true; } /* *下單 */ private function storeOrder($user_id, $goods_id, $number) { try { #開啟事務 DB::beginTransaction(); #查詢庫存sharedLock()共享鎖,可以讀取到資料,事務未提交不能修改,直到事務提交 #lockForUpdate()不能讀取到資料 $resutl = Goods::where(['id'=>$goods_id])->lockForUpdate()->first(); #新增訂單 if ( $resutl ) { $resutl_order = Orders::create([ 'user_id' => $user_id, 'goods_id' => $goods_id, 'goods_number' => $number, 'ordersn' => $this->buildOrderNo(), 'price' => $resutl->price, ]); #減少庫存 $resutl_update = Goods::where('id',$goods_id)->where('store', '>', 0)->decrement('store'); #將使用者從佇列裡面彈出,允許下一個使用者進來 Redis::rpop('user_list'); if ($resutl_order->id > 0 && $resutl_update > 0) { DB::commit(); return true; } } DB::rollBack(); return false; } catch(Exception $e) { throw $e; } } /** * 生成唯一訂單號 */ private function buildOrderNo(){ return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8); } }
轉載:https://blog.csdn.net/xiayu204575/article/details/106719845
搜尋
複製