如何設計高並發時的秒殺,是面試電商技術職位時必考的題目。今天在這里分享一下基於Redis或Memcached的技術方案,能解決重復提交、超發、高並發的問題。
<?php
//預定義總庫存
define("TOTAL_STOCK", 5);
//預定義商品編號
define("ITEM_ID", "ITEM_001");
$userId = $_GET['userId'];
$userIdKey = ITEM_ID . '_' . $userId;
$redis = new redis();
//如果有多台Redis服務器,可根據商品編號哈希后得到其中一台redis的地址
$result = $redis->connect('master104', 6379);
//獲取之前已經領取掉的數量
$requested = $redis->get("requested");
echo "領取前庫存: " . (string)(TOTAL_STOCK - $requested) . "<br />";
//如果已領取大於預定義庫存,則認為庫存為零,不允許繼續
if ($requested && ($requested >= TOTAL_STOCK))
{
echo "已領完,請下次再來";
die();
}
//通過設置用戶對該商品的領取狀態,來檢查該用戶是否已領取過
//如果使用Memcached的話,可以使用cas()
if (!$redis->setnx($userIdKey, 1))
{
echo "您已領取過該商品,不允許重復領取";
die();
}
//增加領取數量以減少庫存。
//高並發情況下可能會有多個incr()是成功的。但是沒關系,在領取數大於庫存數后,通過下面的if判斷后,后面的請求都是無效的。
$requested = $redis->incr("requested");
//如果嘗試增加的時候,發現庫存已經為零了,需要重置用戶領取狀態
if ($requested && ($requested > TOTAL_STOCK))
{
$redis->del($userIdKey);
echo "已領完,請下次再來";
die();
}
//以下可以做其他的后續操作,比如各種異步並行操作,或是投遞消息到隊列,等等
//Step1
//...
//StepN
//如果步驟進行到這里,不管以上的異步操作進行得如何,我們都必須認為用戶已經領取成功。
//即使有任何失敗,我們都需要用技術手段幫用戶完成上述Step1到StepN
echo "領取成功!<br />";
echo "領取后庫存: " . (string)(TOTAL_STOCK - $requested) . "<br />";
?>