PHP 通過redis和mysql實現秒殺業務

2021-10-04 08:23:45 字數 2379 閱讀 2223

$db = mysqldb::getinstance();

$info = $db->fetchrow('select * from goods where goods_id=1');

//判斷是否還有庫存

if ($info['stock'] <= 0)

//減少庫存,num 只是乙個記錄修改資料的次數,可以判斷是否存在超賣現象

$result = $db->update("update goods set stock=stock-1,num=num+1 where goods_id=1 and stock>0");

if (!$result)

exit('搶購成功');

通過apache壓測工具ab來試一試會不會超賣

資料庫情況

ab -c 100 -n 1000
結果

資料庫情況

日誌檔案

沒有超賣

這種方案適合使用者量少的情況,mysql壓力不會太大

監聽key + 事務 實現
具體**

$redis = new \redis();

$redis->connect('127.0.0.1', 6379, 5);

//監聽已搶購的數量,如果在事務執行之前這個 key 被其他命令所改動,那麼事務將被打斷。

$redis->watch('rush:goods_stock:1');

//獲取庫存

$stock = $redis->get('rush:goods_stock:1');

if ($stock == 0)

//事務開始

$redis->multi();

//庫存 -1

$redis->decr('rush:goods_stock:1');

//執行提交

$res = $redis->exec();

//事務提交成功

if ($res)

die('搶購失敗');

設定redis 的值

通過ab壓測

ab -c 100 -n 1000
檢視redis資料情況

這種方案不一定是先提交的先下單,可能會涉及到還有庫存搶購失敗的情況。

通過把庫存設定為redis 佇列 來實現

先設定商品庫存的佇列

$redis = new \redis();

$redis->connect('127.0.0.1', 6379, 5);

//庫存

$stock = 10;

//先清空

$redis->del('rush:goods_stock:queue:2');

//放入佇列中

for ($i = 0; $i < $stock; $i++)

die('放入佇列完成');

執行完成看一下redis資料

實現搶購功能

$redis = new \redis();

$redis->connect('127.0.0.1', 6379, 5);

//讀取佇列,redis操作都是原子性的,不用擔心會重複讀取

if ($redis->rpop('rush:goods_stock:queue:2'))

die('搶購失敗,已經銷售完畢');

壓測

ab -c 100 -n 1000
結果資料正確

該方案使用者基本可以先下手為強。注意設定庫存的地方

庫存解決好了後面就是把訂單寫入資料庫,其實跑乙個指令碼去讀搶購成功的佇列就行了

有的文章是直接判斷搶購成功的佇列是否小於庫存,並且寫入佇列的時候沒有減少庫存。這樣的方式必須等待搶購完畢之後才能寫入資料庫,因為一邊進一邊出,會導致超賣,這種方式不太靈活。也看具體場景吧!

Mac下通過Homebrew安裝MySQL

安裝完成之後,安裝其他的軟體就特別方便了。執行 brew install mysql等成功安裝完成,結果想要登入的時候報了個錯誤 error 2002 hy000 can not connect to local mysql server through socket tmp mysql.sock ...

Mac下通過Homebrew安裝MySQL

安裝完成之後,安裝其他的軟體就特別方便了。執行 brew install mysql等成功安裝完成,結果想要登入的時候報了個錯誤 error 2002 hy000 can not connect to local mysql server through socket tmp mysql.sock ...

Mac下通過Homebrew安裝MySQL

安裝完成之後,安裝其他的軟體就特別方便了。執行 brew install mysql等成功安裝完成,結果想要登入的時候報了個錯誤 error 2002 hy000 can not connect to local mysql server through socket tmp mysql.sock ...