資料庫欄位
1.錯誤的示範
/**
* 錯誤示範
* Create by Peter Yang
* 2021-06-08 10:57:59
* @return string
*/
function test1()
{
//商品id
$id = request()->input('id');
$product = Product::where('id', $id)->firstOrFail();
if ($product->num <= 0) {
return "賣光啦!!";
}
//倉庫減1
$product->decrement('num');
return "success";
}
使用go模擬併發
package main
import (
"fmt"
"github.com/PeterYangs/tools/http"
"sync"
)
func main() {
client := http.Client()
wait := sync.WaitGroup{}
for i := 0; i < 50; i++ {
wait.Add(1)
go func(w *sync.WaitGroup) {
defer wait.Done()
res, _ := client.Request().GetToString("http://www.api/test1?id=1")
fmt.Println(res)
}(&wait)
}
wait.Wait()
}
在資料庫中檢視庫存
庫存已超出
2.redis原子鎖
/**
* redis原子鎖
* Create by Peter Yang
* 2021-06-08 11:00:31
*/
function test2()
{
//商品id
$id = request()->input('id');
$lock = \Cache::lock("product_" . $id, 10);
try {
//最多等待5秒,5秒後未獲取到鎖,則丟擲異常
$lock->block(5);
$product = Product::where('id', $id)->firstOrFail();
if ($product->num <= 0) {
return "賣光啦!!";
}
//倉庫減1
$product->decrement('num');
return 'success';
}catch (LockTimeoutException $e) {
return '當前人數過多';
} finally {
optional($lock)->release();
}
}
庫存正常
3.mysql悲觀鎖
/**
* mysql悲觀鎖
* Create by Peter Yang
* 2021-06-08 11:00:47
*/
function test3()
{
//商品id
$id = request()->input('id');
try {
\DB::beginTransaction();
$product = Product::where('id', $id)->lockForUpdate()->first();
if ($product->num <= 0) {
return "賣光啦!!";
}
//倉庫減1
$product->decrement('num');
\DB::commit();
return "success";
} catch (\Exception $exception) {
}
}
庫存正常
4.mysql樂觀鎖
/**
* mysql樂觀鎖
* Create by Peter Yang
* 2021-06-08 11:00:47
*/
function test4()
{
//商品id
$id = request()->input('id');
$product = Product::where('id', $id)->first();
if ($product->num <= 0) {
return "賣光啦!!";
}
//修改前檢查庫存和之前是否一致,不一致說明已經有變動,則放棄更改
$res = \DB::update('UPDATE `product` SET num = num -1 WHERE id = ? AND num=?', [$id, $product->num]);
if (!$res) {
return '當前人數過多';
}
return 'success';
}
庫存正常
優化樂觀鎖
修改庫存的sql修改為
\DB::update('UPDATE `product` SET num = num -1 WHERE id = ? AND num-1 >= 0', [$id]);
本作品採用《CC 協議》,轉載必須註明作者和本文連結