国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

mysql lock table/unlock table 表鎖/解鎖

2019-11-11 03:32:44
字體:
來源:轉載
供稿:網友

在某個地方看到有個例子,具體描述類似如下:商店現在某商品只有1件庫存,然后A與B在網上進行下訂,A與B幾乎同時(或許也就差幾毫秒,A比B快那么一點點)進行。

很明顯是只有A才能成功下單的,B則會收到庫存不足的提示,但是作為放置在服務端的那個頁面(或者稱為腳本程序)我們得怎樣去處理這個問題呢?或者我先放出一段代碼吧。

 代碼如下復制代碼

    $sql = "select number from goods where id=1";    $number = intval( $db->result( $db->query( $sql ), 0 ) );    if ( $number > 0 ) {    sleep( 2 );    $sql = "update goods set number=number-1 where id = 1";    if ( $db->query( $sql ) ) {    echo 'Ok!Here you are!';    } else {    echo 'Sorry!Something go wrong!Try it again.';    }    } else {    echo 'No more!you are so late!';    }

這部分代碼除了缺少一定注釋外都寫得沒錯,當然$db是一個操作數據庫的類,我只是將大部分方法封裝了,這里的邏輯也是很明顯了。

先獲取id為1這個東東的庫存數,看看是否為0,如果為0就訂購不成功了,如果大于0則將庫存減1然后提示ok。這確實沒有任何錯誤,邏輯也對。如果請求是一個接一個地產生的,那么什么問題都沒有,但當一些并發情況出現時就可能出現一些無厘頭的問題了。你想啊,是不是可能存在一種情況,A剛發出請求,腳本處理到update之前B又發出請求,那么現在庫存依然還有1,因為A的update還沒有執行呢,所以$number不少于0,這次完了,B也下單了,于是庫存變成-1了(假設原來只有1件),確實是一個荒謬而且比較搞笑的結果。

出現問題的原因很明顯,就是忽略了這種并發情況的考慮,處理下訂應該是種隊列方式,也就是先來先得,就是說在執行這個下訂動作是要排隊的,前面的那個先下訂然后后者才能下訂,當然當后者下訂前才再判斷庫存的數量。那么怎樣解決這個問題呢,在程序層面上貌似真的沒有方法去解決這個問題,所以在此才提到鎖表的概念,上面出現這個問題的歸根于沒有控制一個select number的先后順序(或者可以這么說吧),因為在A執行update之前你又允許B去查詢庫存,當然結果還是1,至少要等待A更新庫存后才允許其他人的任何操作,也就是對goods表進行一個排隊操作,對goods表進行鎖定。

說到這里,請不要以為鎖表有多么高深,其實它就是一條sql

    LOCK TABLE `table` [READ|WRITE]

解鎖

    UNLOCK TABLES;

引用專業的描述是

LOCK TABLES為當前線程鎖定表。 UNLOCK TABLES釋放被當前線程持有的任何鎖。當線程發出另外一個LOCK TABLES時,或當服務器的連接被關閉時,當前線程鎖定的所有表會自動被解鎖。 

如果一個線程獲得在一個表上的一個READ鎖,該線程和所有其他線程只能從表中讀。 如果一個線程獲得一個表上的一個WRITE鎖,那么只有持鎖的線程READ或WRITE表,其他線程被阻止。

已經是有種隊列的味道,對不,所以解決方案很簡單嘛,在select前加鎖,執行完后面邏輯代碼后解鎖。或許有沒有人會有一個疑問,就是如果萬一鎖表后線程就斷掉了那么是不是就一直鎖表了,這個確實是可能存在但是既然你想到了那么數據庫的設計人員也一定考慮到了,可以告訴你關于unlock的一些資料:當線程發出另一個 LOCK TABLES,或當與服務器的連接被關閉時,被當前線程鎖定的所有表將被自動地解鎖。這下放心了吧。

好,看下改進后的代碼。

   

 代碼如下復制代碼
$db->lock( 'goods', 2 );    $sql = "select number from goods where id=1";    $number = intval( $db->result( $db->query( $sql ), 0 ) );    if ( $number > 0 ) {    sleep( 2 );    $sql = "update goods set number=number-1 where id = 1";    if ( $db->query( $sql ) ) {    echo 'Ok!Here you are!';    } else {    echo 'Sorry!Something go wrong!Try it again.';    }    } else {    echo 'No more!you are so late!';    }    $db->unlock();

只加了兩行代碼,不過也不能這么說,因為paperen我修改了自己那個操作數據庫的類,加了兩個方法lock與unlock,其實這兩個方法也很簡單。

 

 代碼如下復制代碼
   /**    * 鎖表    * @param string $table 表名    * @param int $type 讀鎖1還是寫鎖2    */    public function lock( $table, $type = 1 ) {    $type = ( $type == 1 ) ? 'READ' : 'WRITE';    $this->query( "LOCK TABLE `$table` $type" );    }         /**    * 解鎖    */    public function unlock() {    $this->query( "UNLOCK TABLES" );    }

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 龙岩市| 行唐县| 岳西县| 博白县| 林芝县| 黎川县| 洛扎县| 弥勒县| 星座| 那曲县| 勐海县| 万全县| 华容县| 红安县| 重庆市| 望都县| 鄂托克前旗| 桐庐县| 安平县| 依兰县| 水富县| 吴忠市| 永修县| 丹棱县| 双牌县| 中牟县| 三亚市| 喀喇| 化德县| 年辖:市辖区| 监利县| 霍城县| 紫云| 宣恩县| 南阳市| 清涧县| 郑州市| 襄樊市| 诸城市| 肥东县| 长葛市|