1. 秒杀功能主要有两个问题要解决: ①高并发对数据库产生的压力 ②竞争状态下如何解决库存的正确减少(“超卖” 问题)。 第一个问题,对于 PHP 来说很简单,用缓存技术就可以缓解数据库压力,比如 memcache,redis 等缓存技术,这里我使用了 redis。 第二个问题,我使用 redis 队列,因为 pop 操作是原子的,即使有很多用户同时到达,也是依次执行。
2. 对于第二个问题,我觉得可以使用 Laravel 的任务调度功能,每分钟运行一次脚本,把超时的订单从数据库中删除掉,同时将库存加 1
Route::get('/home', 'HomeController@index')->name('home');
Route::get('/goods-store', 'HomeController@goodsNumberQueue')->name('goods-store');
Route::get('/order', 'HomeController@secKill')->name('order');
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Redis;
class HomeController extends Controller
{
/**
* Show the application dashboard.
*
* @return \Illuminate\Contracts\Support\Renderable
*/
public $msg = '';
/**
* Create a new controller instance.
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
$goodsId = 1; //商品ID号,这里我写死为1
$this->user_queue_key = "goods:{$goodsId}:user"; //抢购成功队列的key,使用redis的hashes方式存储
$this->goods_number_key = "goods:{$goodsId}"; //商品库存key,使用redis的lists方式存储
}
/**
* Show the application dashboard.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$orders = Redis::hgetall($this->user_queue_key);
foreach ($orders as $key => $order) {
$orders[$key] = unserialize($order);
}
return view('home', compact('orders'));
}
/**
* 开始秒杀
*
*/
public function secKill()
{
$isHaveQueue = Redis::hexists($this->user_queue_key, auth()->id());
if ($isHaveQueue) {
return back()->with('msg', '您已抢购!');
}
$count = Redis::rpop($this->goods_number_key);
if (!$count) {
return back()->with('msg', '已经抢光了哦!');
}
$this->succUserNumberQueue(auth()->id());
return back()->with('msg', '抢购成功!');
}
/**
* 抢购结果队列,将抢购成功的客户加入队列
*
* @return bool
*/
private function succUserNumberQueue($uid)
{
$orderNo = $this->buildOrderNo();
$userQueue = [
'user_id' => auth()->id(),
'user_name' => \Auth::user()->name,
'order_no' => $orderNo,
'created_at' => date('Y-m-d H:i:s'),
];
$userQueue = serialize($userQueue);
Redis::hset($this->user_queue_key, $uid, $userQueue);
return true;
}
//生成唯一订单号
private function buildOrderNo()
{
return date('ymd').substr(implode(NULL, array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
}
/**
* 将商品库存加入redis,生成库存
*
*/
public function goodsNumberQueue()
{
$store = 100;
$goodsLen = Redis::llen($this->goods_number_key);
$count = $store - $goodsLen <= 0 ? 0 : $store - $goodsLen;
for($i = 0; $i < $count; $i++) {
Redis::lpush($this->goods_number_key, 1);
}
echo '目前库存:' . Redis::llen($this->goods_number_key) . '<br/>';
}
}

本文详细介绍了秒杀系统的两大核心问题:高并发下数据库压力缓解及库存正确减少的解决方案。利用Redis缓存技术和队列操作,有效解决了高并发场景下的数据库压力,并通过原子性操作避免了超卖问题。

543

被折叠的 条评论
为什么被折叠?



