久久久久久久视色,久久电影免费精品,中文亚洲欧美乱码在线观看,在线免费播放AV片

<center id="vfaef"><input id="vfaef"><table id="vfaef"></table></input></center>

    <p id="vfaef"><kbd id="vfaef"></kbd></p>

    
    
    <pre id="vfaef"><u id="vfaef"></u></pre>

      <thead id="vfaef"><input id="vfaef"></input></thead>

    1. 站長資訊網(wǎng)
      最全最豐富的資訊網(wǎng)站

      PHP高并發(fā)實例詳解之解決商品庫存超賣問題

      本篇文章給大家?guī)砹岁P(guān)于PHP的相關(guān)知識,其中主要介紹了關(guān)于在高并發(fā)情況下防止商品庫存超賣的相關(guān)問題,主要解決高并發(fā)對數(shù)據(jù)庫產(chǎn)生的壓力以及競爭狀態(tài)下如何解決商品庫存超賣,希望對大家有幫助。

      PHP高并發(fā)實例詳解之解決商品庫存超賣問題

      推薦學(xué)習:《PHP教程》

      商城系統(tǒng)中,搶購和秒殺是很常見的營銷場景,在一定時間內(nèi)有大量的用戶訪問商場下單,主要需要解決的問題有兩個:

      • 高并發(fā)對數(shù)據(jù)庫產(chǎn)生的壓力;

      • 競爭狀態(tài)下如何解決商品庫存超賣;

      高并發(fā)對數(shù)據(jù)庫產(chǎn)生的壓力

      對于第一個問題,使用緩存來處理,避免直接操作數(shù)據(jù)庫,例如使用 Redis。

      競爭狀態(tài)下如何解決商品庫存超賣

      對于第二個問題,需要重點說明。

      常規(guī)寫法:查詢出對應(yīng)商品的庫存,判斷庫存數(shù)量否大于 0,然后執(zhí)行生成訂單等操作,但是在判斷庫存是否大于 0 處,如果在高并發(fā)下就會有問題,導(dǎo)致庫存量出現(xiàn)負數(shù)。

      測試表 sql

      把如下表數(shù)據(jù)導(dǎo)入到數(shù)據(jù)庫中

      /* Navicat MySQL Data Transfer Source Server         : 01 本地localhost Source Server Version : 50553 Source Host           : localhost:3306 Source Database       : test Target Server Type    : MYSQL Target Server Version : 50553 File Encoding         : 65001 Date: 2020-11-06 14:31:35 */ SET FOREIGN_KEY_CHECKS=0; -- ---------------------------- -- Table structure for products -- ---------------------------- DROP TABLE IF EXISTS `products`; CREATE TABLE `products` (   `id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'ID',   `title` varchar(50) DEFAULT NULL COMMENT '貨品名稱',   `store` int(11) DEFAULT '0' COMMENT '貨品庫存',   PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COMMENT='貨品表'; -- ---------------------------- -- Records of products -- ---------------------------- INSERT INTO `products` VALUES ('1', '稻花香大米', '20'); -- ---------------------------- -- Table structure for order_log -- ---------------------------- DROP TABLE IF EXISTS `order_log`; CREATE TABLE `order_log` (   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,   `content` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL COMMENT '日志內(nèi)容',   `c_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '創(chuàng)建時間',   PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1; -- ---------------------------- -- Table structure for order -- ---------------------------- DROP TABLE IF EXISTS `order`; CREATE TABLE `order` (   `oid` bigint(20) unsigned NOT NULL DEFAULT '0' COMMENT '訂單號',   `product_id` int(11) DEFAULT '0' COMMENT '商品ID',   `number` int(11) DEFAULT '0' COMMENT '購買數(shù)量',   `c_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '創(chuàng)建時間',   PRIMARY KEY (`oid`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='訂單表';

      下單處理代碼

      <?php db(); global $con; //step1 接收下單參數(shù) $product_id = 1;// 商品ID $buy_num = 1;// 購買數(shù)量 //step2 查詢商品信息 $sql = "select * from products where id={$product_id}"; $result = mysqli_query($con, $sql); $row = mysqli_fetch_assoc($result); //step3 判斷商品下單數(shù)量是否大于商品庫存數(shù)量 //此處在高并發(fā)下,可能出現(xiàn)上一個下單后還沒來得及更新庫存,下一個下單判斷庫存數(shù)不是最新的庫存 if ($row['store'] > 0) {     sleep(1);     //step4 更新商品庫存數(shù)量(減去下單數(shù)量)     $sql = "update products set store=store-{$buy_num} where id={$product_id}";     if (mysqli_query($con, $sql)) {         echo "更新成功";         //step5 生成訂單號創(chuàng)建訂單         $oid = build_order_no();         create_order($oid, $product_id, $buy_num);         insertLog('庫存減少成功,下單成功');     } else {         echo "更新失敗";         insertLog('庫存減少失敗');     } } else {     echo "沒有庫存";     insertLog('庫存不夠'); } function db() {     global $con;     $con = new mysqli('localhost','root','root','test');     if (!$con) {         echo "數(shù)據(jù)庫連接失敗";     } } /**  * 生成唯一訂單號  */ function build_order_no() {     return date('Ymd') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT); } function create_order($oid, $product_id, $number) {     global $con;     $sql = "INSERT INTO `order` (oid, product_id, number) values('$oid', '$product_id', '$number')";     mysqli_query($con, $sql); } /**  * 記錄日志  */ function insertLog($content) {     global $con;     $sql = "INSERT INTO `order_log` (content) values('$content')";     mysqli_query($con, $sql); }

      將庫存字段字段設(shè)為 unsigned

      因為庫存字段不能為負數(shù),在下單后更新商品庫存時,如果出現(xiàn)負數(shù)將返回 false

      <?php db(); global $con; //step1 接收下單參數(shù) $product_id = 1;// 商品ID $buy_num = 1;// 購買數(shù)量 //step2 查詢商品信息 $sql = "select * from products where id={$product_id} for UPDATE";//利用for update 開啟行鎖 $result = mysqli_query($con, $sql); $row = mysqli_fetch_assoc($result); //step3 判斷商品下單數(shù)量是否大于商品庫存數(shù)量 if ($row['store'] > 0) {     sleep(1);     //step4 更新商品庫存數(shù)量(減去下單數(shù)量)     $sql = "update products set store=store-{$buy_num} where id={$product_id}";     if (mysqli_query($con, $sql)) {         echo "更新成功";         //step5 生成訂單號創(chuàng)建訂單         $oid = build_order_no();         create_order($oid, $product_id, $buy_num);         insertLog('庫存減少成功,下單成功');     } else {         // 如果出現(xiàn)負數(shù)將返回false         echo "更新失敗";         insertLog('庫存減少失敗');     } } else {     //商品已經(jīng)搶購?fù)?    echo "沒有庫存";     insertLog('庫存不夠'); } function db() {     global $con;     $con = new mysqli('localhost','root','root','test');     if (!$con) {         echo "數(shù)據(jù)庫連接失敗";     } } /**  * 生成唯一訂單號  */ function build_order_no() {     return date('Ymd') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT); } function create_order($oid, $product_id, $number) {     global $con;     $sql = "INSERT INTO `order` (oid, product_id, number) values('$oid', '$product_id', '$number')";     mysqli_query($con, $sql); } /**  * 記錄日志  */ function insertLog($content) {     global $con;     $sql = "INSERT INTO `order_log` (content) values('$content')";     mysqli_query($con, $sql); }

      使用 mysql 的事務(wù),鎖住操作的行

      在下單處理過程中,使用 mysql 的事務(wù)將正在下單商品行數(shù)據(jù)鎖定

      <?php db(); global $con; //step1 接收下單參數(shù) $product_id = 1;// 商品ID $buy_num = 1;// 購買數(shù)量 mysqli_query($con, "BEGIN"); //開始事務(wù) //step2 查詢商品信息 $sql = "select * from products where id={$product_id} for UPDATE";//利用for update 開啟行鎖 $result = mysqli_query($con, $sql); $row = mysqli_fetch_assoc($result); //step3 判斷商品下單數(shù)量是否大于商品庫存數(shù)量 if ($row['store'] > 0) {     sleep(1);     //step4 更新商品庫存數(shù)量(減去下單數(shù)量)     $sql = "update products set store=store-{$buy_num} where id={$product_id}";     if (mysqli_query($con, $sql)) {         echo "更新成功";         //step5 生成訂單號創(chuàng)建訂單         $oid = build_order_no();         create_order($oid, $product_id, $buy_num);         insertLog('庫存減少成功,下單成功');         mysqli_query($con, "COMMIT");//事務(wù)提交即解鎖     } else {         echo "更新失敗";         insertLog('庫存減少失敗');         mysqli_query($con, "ROLLBACK");//事務(wù)回滾即解鎖     } } else {     //商品已經(jīng)搶購?fù)?    echo "沒有庫存";     insertLog('庫存不夠');     mysqli_query($con, "ROLLBACK");//事務(wù)回滾即解鎖 } function db() {     global $con;     $con = new mysqli('localhost','root','root','test');     if (!$con) {         echo "數(shù)據(jù)庫連接失敗";     } } /**  * 生成唯一訂單號  */ function build_order_no() {     return date('Ymd') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT); } function create_order($oid, $product_id, $number) {     global $con;     $sql = "INSERT INTO `order` (oid, product_id, number) values('$oid', '$product_id', '$number')";     mysqli_query($con, $sql); } /**  * 記錄日志  */ function insertLog($content) {     global $con;     $sql = "INSERT INTO `order_log` (content) values('$content')";     mysqli_query($con, $sql); }

      使用非阻塞的文件排他鎖

      在處理下單請求的時候,用 flock 鎖定一個文件,如果鎖定失敗說明有其他訂單正在處理,此時要么等待要么直接提示用戶” 服務(wù)器繁忙”,計數(shù)器存儲搶購的商品數(shù)量,避免查詢數(shù)據(jù)庫。

      阻塞 (等待) 模式:并發(fā)時,當有第二個用戶請求時,會等待第一個用戶請求完成、釋放鎖,獲得文件鎖之后,程序才會繼續(xù)運行下去。

      <?php db(); global $con; //step1 接收下單參數(shù) $product_id = 1;// 商品ID $buy_num = 1;// 購買數(shù)量 $fp = fopen('lock.txt', 'w'); if (flock($fp, LOCK_EX)) {   //文件獨占鎖,阻塞     //step2 查詢商品信息     $sql = "select * from products where id={$product_id}";     $result = mysqli_query($con, $sql);     $row = mysqli_fetch_assoc($result);     //step3 判斷商品下單數(shù)量是否大于商品庫存數(shù)量     if ($row['store'] > 0) {         //處理訂單         sleep(1);         //step4 更新商品庫存數(shù)量(減去下單數(shù)量)         $sql = "update products set store=store-{$buy_num} where id={$product_id}";         if (mysqli_query($con, $sql)) {             echo "更新成功";             //step5 生成訂單號創(chuàng)建訂單             $oid = build_order_no();             create_order($oid, $product_id, $buy_num);             insertLog('庫存減少成功,下單成功');         } else {             echo "更新失敗";             insertLog('庫存減少失敗');         }     } else {         //商品已經(jīng)搶購?fù)?        echo "沒有庫存";         insertLog('庫存不夠');     }     flock($fp, LOCK_UN); //釋放鎖 } fclose($fp); function db() {     global $con;     $con = new mysqli('localhost','root','root','test');     if (!$con) {         echo "數(shù)據(jù)庫連接失敗";     } } /**  * 生成唯一訂單號  */ function build_order_no() {     return date('Ymd') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT); } function create_order($oid, $product_id, $number) {     global $con;     $sql = "INSERT INTO `order` (oid, product_id, number) values('$oid', '$product_id', '$number')";     mysqli_query($con, $sql); } /**  * 記錄日志  */ function insertLog($content) {     global $con;     $sql = "INSERT INTO `order_log` (content) values('$content')";     mysqli_query($con, $sql); }

      非阻塞模式:并發(fā)時,第一個用戶請求,拿得文件鎖之后。后面請求的用戶直接返回系統(tǒng)繁忙,請稍后再試

      <?php db(); global $con; //step1 接收下單參數(shù) $product_id = 1;// 商品ID $buy_num = 1;// 購買數(shù)量 $fp = fopen('lock.txt', 'w'); if (flock($fp, LOCK_EX|LOCK_NB)) {   //文件獨占鎖,非阻塞     //step2 查詢商品信息     $sql = "select * from products where id={$product_id}";     $result = mysqli_query($con, $sql);     $row = mysqli_fetch_assoc($result);     //step3 判斷商品下單數(shù)量是否大于商品庫存數(shù)量     if ($row['store'] > 0) {         //處理訂單         sleep(1);         //step4 更新商品庫存數(shù)量(減去下單數(shù)量)         $sql = "update products set store=store-{$buy_num} where id={$product_id}";         if (mysqli_query($con, $sql)) {             echo "更新成功";             //step5 生成訂單號創(chuàng)建訂單             $oid = build_order_no();             create_order($oid, $product_id, $buy_num);             insertLog('庫存減少成功,下單成功');         } else {             echo "更新失敗";             insertLog('庫存減少失敗');         }     } else {         //商品已經(jīng)搶購?fù)?        echo "沒有庫存";         insertLog('庫存不夠');     }     flock($fp, LOCK_UN); //釋放鎖 } else {     //系統(tǒng)繁忙,請稍后再試     echo "系統(tǒng)繁忙,請稍后再試";     insertLog('系統(tǒng)繁忙,請稍后再試'); } fclose($fp); function db() {     global $con;     $con = new mysqli('localhost','root','root','test');     if (!$con) {         echo "數(shù)據(jù)庫連接失敗";     } } /**  * 生成唯一訂單號  */ function build_order_no() {     return date('Ymd') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT); } function create_order($oid, $product_id, $number) {     global $con;     $sql = "INSERT INTO `order` (oid, product_id, number) values('$oid', '$product_id', '$number')";     mysqli_query($con, $sql); } /**  * 記錄日志  */ function insertLog($content) {     global $con;     $sql = "INSERT INTO `order_log` (content) values('$content')";     mysqli_query($con, $sql); }

      因為 pop 操作是原子的,即使有很多用戶同時到達,也是依次執(zhí)行,推薦使用

      mysql 事務(wù)在高并發(fā)下性能下降很厲害,文件鎖的方式也是

      先將商品庫存到 redis 隊列

      <?php db(); global $con; // 查詢商品信息 $product_id = 1; $sql = "select * from products where id={$product_id}"; $result = mysqli_query($con, $sql); $row = mysqli_fetch_assoc($result); $store = $row['store']; // 獲取商品在redis緩存的庫存 $redis = new Redis(); $result = $redis->connect('127.0.0.1', 6379); $key = 'goods_store_' . $product_id; $res = $redis->llen($key); $count = $store - $res; for ($i=0; $i<$count; $i++) {     $redis->lpush($key, 1); } echo $redis->llen($key); function db() {     global $con;     $con = new mysqli('localhost','root','root','test');     if (!$con) {         echo "數(shù)據(jù)庫連接失敗";     } }

      2. 搶購、秒殺邏輯

      <?php db(); global $con; //step1 接收下單參數(shù) $product_id = 1;// 商品ID $buy_num = 1;// 購買數(shù)量 //step2 下單前判斷redis隊列庫存量 $redis = new Redis(); $result = $redis->connect('127.0.0.1',6379); $count = $redis->lpop('goods_store_' . $product_id); if (!$count) {     insertLog('error:no store redis');     return '秒殺結(jié)束,沒有商品庫存了'; } sleep(1); //step3 更新商品庫存數(shù)量(減去下單數(shù)量) $sql = "update products set store=store-{$buy_num} where id={$product_id}"; if (mysqli_query($con, $sql)) {     echo "更新成功";     //step4 生成訂單號創(chuàng)建訂單     $oid = build_order_no();     create_order($oid, $product_id, $buy_num);     insertLog('庫存減少成功,下單成功'); } else {     echo "更新失敗";     insertLog('庫存減少失敗'); } function db() {     global $con;     $con = new mysqli('localhost','root','root','test');     if (!$con) {         echo "數(shù)據(jù)庫連接失敗";     } } /**  * 生成唯一訂單號  */ function build_order_no() {     return date('Ymd') . str_pad(mt_rand(1, 99999), 5, '0', STR_PAD_LEFT); } function create_order($oid, $product_id, $number) {     global $con;     $sql = "INSERT INTO `order` (oid, product_id, number) values('$oid', '$product_id', '$number')";     mysqli_query($con, $sql); } /**  * 記錄日志  */ function insertLog($content) {     global $con;     $sql = "INSERT INTO `order_log` (content) values('$content')";     mysqli_query($con, $sql); }

      redis 樂觀鎖防止超賣

      <?php $redis =new Redis(); $redis->connect("127.0.0.1", 6379); $redis->watch('sales');//樂觀鎖 監(jiān)視作用 set()  初始值0 $sales = $redis->get('sales'); $n = 20;// 庫存 if ($sales >= $n) {     exit('秒殺結(jié)束'); } //redis開啟事務(wù) $redis->multi(); $redis->incr('sales'); //將 key 中儲存的數(shù)字值增一 ,如果 key 不存在,那么 key 的值會先被初始化為 0 ,然后再執(zhí)行 INCR 操作。 $res = $redis->exec(); //成功1 失敗0 if ($res) {     //秒殺成功     $con = new mysqli('localhost','root','root','test');     if (!$con) {         echo "數(shù)據(jù)庫連接失敗";     }     $product_id = 1;// 商品ID     $buy_num = 1;// 購買數(shù)量     sleep(1);     $sql = "update products set store=store-{$buy_num} where id={$product_id}";     if (mysqli_query($con, $sql)) {         echo "秒殺完成";     } } else {     exit('搶購失敗'); }

      推薦學(xué)習:《PHP視頻教程》

      贊(0)
      分享到: 更多 (0)
      網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號