1. 程式人生 > >下單高併發介面

下單高併發介面

下單介面高併發

模擬高併發情況下保證不超售,
秒殺數量 避免負庫存的出現。

  • 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";
}