下單高併發介面
阿新 • • 發佈:2019-01-02
下單介面高併發
模擬高併發情況下保證不超售,
秒殺數量 避免負庫存的出現。
- 1.資料庫加鎖(效能損耗)
- 2.Redis快取佇列
- 所以使用Redis快取佇列
Redis
Window 下安裝 要區分 32位和 64位
# 目錄啟動 程式執行
redis-server.exe redis.windows.conf
# 另視窗啟動 命令列測試
redis-cli.exe -h 127.0.0.1 -p 6379
# quick啟動不能用 redis,要配置域名
php -S localhost:8233 router.php
php 安裝Redis擴充套件
Apach 模擬高併發
# 在apache\bin 目錄下執行 ab -n 請求的次數 -c 多少人請求 被請求地址 # example,域名後面 / 必須 ab -n 600 -c 600 http://redis-tp5.hd/
CREATE TABLE `redis_product` ( `id` int(10) NOT NULL AUTO_INCREMENT, `name` varchar(50) NOT NULL COMMENT '商品名稱', `seckill_count` int(10) NOT NULL COMMENT '商品數量', `create_time` int(10) NOT NULL, `update_time` int(10) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='商品表';
人口CREATE TABLE `redis_sec_kill_product` ( `id` int(10) NOT NULL AUTO_INCREMENT, `user_id` int(10) NOT NULL, `product_id` int(10) NOT NULL, `create_time` int(10) NOT NULL, `update_time` int(10) NOT NULL, PRIMARY KEY (`id`), KEY `product_id` (`product_id`), CONSTRAINT `product_id` FOREIGN KEY (`product_id`) REFERENCES `redis_product` (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='秒殺列表';
public function index(){ //$this->redisDel('secKillCount:1'); //$this->redisDel();exit; //------------------------- // 商品ID $productId = 1; // 秒殺數量 $secKillCount = $this->getRedisSecKillCount($productId); // 使用者ID $userId = rand(1,9999); $res = self::secKill($productId, $secKillCount, $userId ); if($res['state']){ return $res['msg']; }else{ // 搶購成功的使用者ID if( !empty($res['data']) ){ // 訂單入庫 $secKillProducts = []; foreach ($res['data'] as $uid) { $secKillProducts[] = [ 'user_id' => $uid, 'product_id'=> $productId ]; } // 全部搶完再入庫? $SecKillProduct = new SecKillProduct(); $resSK = $SecKillProduct->saveAll($secKillProducts); $resPro = Product::update([ 'id'=>$productId, // 秒殺庫存設定0 'seckill_count' =>0, ]); if($res && $resPro){ // 所有 訂單建立成功 } } return $res['msg']; } }
/** * 秒殺、 * @param $productId * @param int $killCount * @param $userId * @return array */ public static function secKill($productId, $killCount = 0, $userId){ // 秒殺key 商品id $SecKillKey = 'order:'.$productId; $redis = new \Redis(); // 連線 $redis->connect(config('redis.host'), config('redis.port')); //儲存資料到列表中 if( $redis->lLen($SecKillKey) < $killCount ){ // Redis 入列 $redis->lPush($SecKillKey, $userId); return [ 'msg' => '恭喜您!搶到了哦', 'state' => 1, 'user_id' => $userId, ]; }else{ $uid_list = []; // 秒殺結束 if( $redis->lLen($SecKillKey) == $killCount ){ //取前幾 $uid_list = $redis->lrange($SecKillKey, 0 ,$killCount); $redis->lPush($SecKillKey, 'fill'); // 過期時間 100秒 //$redis->expire($SecKillKey,100); } return [ 'msg' => '已經搶光了哦', 'state' => 0, 'user_id' => $userId, 'data' => $uid_list, // 使用者ID list ]; } } /** * 獲取秒殺數量,使用redis 快取 * @param $productId * @return bool|string */ private function getRedisSecKillCount($productId){ $secKillCountKey = 'secKillCount:'.$productId; $redis = new \Redis(); $redis->connect(config('redis.host'), config('redis.port')); // 判斷Redis 快取是否存在 if( !$redis->get($secKillCountKey) ){ $res = Product::get($productId); $redis->set($secKillCountKey, $res['seckill_count']); return $res['seckill_count']; } return $redis->get($secKillCountKey); } // 手動 刪除用 private function redisDel($delKey = 'order:1'){ $redis = new \Redis(); // 指令碼結束之後連線就釋放了? $redis->connect('127.0.0.1', 6379); $redis->delete($delKey); var_dump($redis->lLen('order:1')); echo "刪除 {$delKey} redis key"; }