<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                一、首先來說一下什么是共享鎖?什么是排他鎖? 共享:我可以讀 寫 加鎖 , 別人可以 讀 加鎖。 排他:只有我 才 可以 讀 寫 加鎖 , 也就是說,必須要等我提交事務,其他的才可以操作。 二、簡單例子實現加鎖 鎖和事務在使用時需要配合使用,也就是用鎖時需要先開啟事務,事務提交時,會自動解鎖。 ``` DB::beginTransaction(); // 開啟事務 $good = \App\Models\Good::sharedLock()->first(); //共享鎖 s鎖 讀鎖 // $good = \App\Models\Good::lockForUpdate()->first(); //排他鎖 x鎖 寫鎖... DB::commit(); DB::beginTransaction(); $goodsInfo = Goods::where('goods_id',$gid)->lockForUpdate()->first(); $goodsInfo->seckill_stock-=1; $goodsInfo->save(); DB::commit(); ``` 三、怎樣利用鎖和事務解決并發問題? 在我們的工作中,常常會出現一些對數量控制有精確要求的需求,比如商品庫存量、獎品數量、報名人數限制等等,這些應用場景往往都存在高并發可能,比較容易出現數據量超量問題。以下做一下示例探索: (1)首先設計一個存量表 ``` CREATE TABLE `product` ( `id` int(11) NOT NULL AUTO_INCREMENT, `product_name` varchar(255) NOT NULL DEFAULT '', `count` int(10) NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; ``` (2)添加一行數據如下,設定基礎庫存量為 10 (3)問題代碼如下: ``` $process_num = 50; //開50個進程,模擬50個用戶 for ($i = 0; $i < $process_num; $i++) { MultiProcessHelper::instance($process_num)->multiProcessTask(function () use ($i) { if (Db::name('product')->where('id', 1)->value('count') > 0) { $res = Db::name('product')->where('id', 1)->setDec('count'); if ($res) { dump('獲取到更新資源權限:' . $i); } } }); } ``` 執行結果,50 個用戶都獲取到了更新資源的權限,但是數據庫相應數據存量變成了 - 40 高并發帶來的問題,同一時刻有多個進程讀取同一條數據,同一時刻有多個進程更新同一條數據 (4)解決方案 1.方案1 要進行 DML 層面的限制(最后關卡安全,報錯總比出現數據問題產生的影響小),主要的修改是將 count 的類型改成了無符號整數,這樣該值就不可能再出現負數值 ``` CREATE TABLE `product` ( `id` int(11) NOT NULL AUTO_INCREMENT, `product_name` varchar(255) NOT NULL DEFAULT '', `count` int(10) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4; ``` 執行一下代碼,當 count 值從 10 減到 0 時,就不能再減少了,再減就會出現數據庫報錯 2.方案2 mysql 提供的行級鎖 select ... lock in share mode(阻塞寫),select ... for update(阻塞讀寫,悲觀鎖),所以 for update 機制能滿足我們的原子要求。編輯代碼如下: ``` $process_num = 50; //開50個進程,模擬50個用戶 for ($i = 0; $i < $process_num; $i++) { MultiProcessHelper::instance($process_num)->multiProcessTask(function () use ($i) { Db::startTrans(); //行級鎖必須在事務中才能生效 //設置for update,進程會阻塞在這里,只能允許一個進程獲取到行鎖,其他等待獲取 if (Db::name('product')->where('id', 1)->lock('for update')->value('count') > 0) { $res = Db::name('product')->where('id', 1)->setDec('count'); if ($res) { dump('獲取到更新資源權限:' . $i); } } Db::commit(); }); } ``` 只有十個進程獲取到了更新權限,消費正常 3.方案3 將條件語句放到 update 上,保持語句執行的原子性,杜絕并發幻讀 修改代碼如下: ``` $process_num = 50; //開50個進程,模擬50個用戶 for ($i = 0; $i < $process_num; $i++) { MultiProcessHelper::instance($process_num)->multiProcessTask(function () use ($i) { //合并兩條語句為一條更新語句 $res = Db::name('product')->where('id', 1)->where('count', '>', 0)->setDec('count'); if ($res) { dump('獲取到更新資源權限:' . $i); } }); } ``` 只有十個進程獲取到了更新權限,消費正常 4.方案4 文件鎖機制解決 ``` $process_num = 50; //開50個進程,模擬50個用戶 for ($i = 0; $i < $process_num; $i++) { MultiProcessHelper::instance($process_num)->multiProcessTask(function () use ($i) { $filename = app()->getRootPath() . 'runtime/lock'; $file = fopen($filename, 'w'); //打開文件 $lock = flock($file, LOCK_EX); // $lock=flock($handle, LOCK_EX|LOCK_NB); (異步非阻塞,所有進程如果出現獲取不到鎖,不等待跳過,加鎖失敗) //獲取文件排他鎖:LOCK_EX(異步阻塞,只有一個進程獲得鎖,其他競爭進程等待) //還有一種共享鎖:LOCK_SH(所有進程都可以獲取共享鎖,讀取文件,當且只有一個鎖時,才允許寫操作,否則操作失敗,容易出現死鎖問題) if ($lock) { try { if (Db::name('product')->where('id', 1)->lock('for update')->value('count') > 0) { $res = Db::name('product')->where('id', 1)->setDec('count'); if ($res) { dump('獲取到更新資源權限:' . $i); } } } catch (\Exception $e) { dump($e->getMessage()); } finally { flock($file, LOCK_UN); //無論如何都要釋放鎖 } } fclose($file); //關閉文件句柄 }); } ``` 只有十個進程獲取到了更新權限,消費正常 5.方案5 分布式鎖機制解決 以上文件鎖,只適應于單體架構的需求,在集群架構、分布式等多機聯網結構中就是掩耳盜鈴了,所以適應性更好地鎖機制還是要使用分布式鎖,分布式鎖最常用和最易用就是 redis 的 setnx 鎖了。 ``` $process_num = 50; //開50個進程,模擬50個用戶 for ($i = 0; $i < $process_num; $i++) { MultiProcessHelper::instance($process_num)->multiProcessTask(function () use ($i) { //獲取redis鎖 //關于CacheHelper::getRedisLock是怎樣獲取鎖的,注意幾個點就行:1.如何避免死鎖;2.如何設置過期時間;3.如何設置搶占條件;4.如何循環等待判斷。這些不在本文討論范圍,可自行研究,以后有空我也可以寫一篇博文 $lock = CacheHelper::getRedisLock('redis_lock'); if ($lock) { try { if (Db::name('product')->where('id', 1)->lock('for update')->value('count') > 0) { $res = Db::name('product')->where('id', 1)->setDec('count'); if ($res) { dump('獲取到更新資源權限:' . $i); } } } catch (\Exception $e) { dump($e->getMessage()); } } else { // dump('獲取redis鎖失敗'); } }); } ``` ————————————————
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看