MySQL5.5 版本以後默認採用innoDb 數據引擎.本文采用默認的存儲引擎。
樂觀鎖其實是一種邏輯思想,並非mysql 數據庫的特性。這個要區分清楚。 實現數據版本有兩種方式,第一種是使用版本號,第二種是使用時間戳。
使用方式::mysql
/** 僞代碼 number 庫存 goods_id 商品ID version 版本號默認爲0 **/ $sql="select number from goods where goods_id={$goods_id} and version= {$version} "; // 時間戳方式查詢庫存 $sql="select number from goods where goods_id={$goods_id} and time < time() "; $rs=mysqli_query($conn,$sql); $row = $rs->fetch_assoc(); if($row['number']>0){//高併發下會致使超賣 if($row['number']<$number){ return insertLog('庫存不夠',3,$username); } //庫存減小 $sql="update goods set number=number-{$number},version += 1 where goods_id={$goods_id} and number>0"; // 時間戳方式減小庫存 $sql="update goods set number=number-{$number},time = time() where goods_id={$goods_id} and number>0"; $store_rs=mysqli_query($conn,$sql); if($store_rs){ //生成訂單,返回操做成功 echo json_encode(array('code'=>0,'msg'=>'庫存減小成功','data'=>$username)); }else{ echo json_encode(array('code'=>1,'msg'=>'庫存減小失敗','data'=>$username)); } }else{ echo json_encode(array('code'=>3,'msg'=>'庫存減小失敗','data'=>$username)); }
注意:使用樂觀鎖須要注意啊,將庫存字段number字段設爲unsigned,當庫存爲0時,由於字段不能爲負數,將會返回false
悲觀鎖指的是對數據被外界(包括本系統當前的其餘事務,以及來自外部系統的事務處理)修改持保守態度,所以,在整個數據處理過程當中,將數據處於鎖定狀態。悲觀鎖的實現,每每依靠數據庫提供的鎖機制(也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,不然,即便在本系統中實現了加鎖機制,也沒法保證外部系統不會修改數據)。悲觀鎖利用了MySQL innoDB 存儲引擎的支持行鎖的特性。一行一行操做數據.sql
使用方式:數據庫
// 使用悲觀鎖鎖住當前行 $sql="select number from goods where goods_id={$goods_id} for update "; // 減小庫存操做 $sql="update goods set number=number-{$number} where goods_id={$goods_id} and number>0";
注意:innoDB 行鎖是基於索引來執行的,where 條件後必須有索引,否則走的就是全表掃描。
優勢與不足:json
悲觀併發控制其實是「先取鎖再訪問」的保守策略,爲數據處理的安全提供了保證。可是在效率方面,處理加鎖的機制會讓數據庫產生額外的開銷,還有增長產生死鎖的機會;另外,在只讀型事務處理中因爲不會產生衝突,也不必使用鎖,這樣作只能增長系統負載;還有會下降了並行性,一個事務若是鎖定了某行數據,其餘事務就必須等待該事務處理完才能夠處理那行數樂觀併發控制相信事務之間的數據競爭(datarace)的機率是比較小的,所以儘量直接作下去,直到提交的時候纔去鎖定,因此不會產生任何鎖和死鎖。但若是直接簡單這麼作,仍是有可能會遇到不可預期的結果,例如兩個事務都讀取了數據庫的某一行,通過修改之後寫回數據庫,這時就遇到了問題。安全
建議: 在用戶併發量不大的應用場景下,採用樂觀鎖的方式。在對數據一致性要求很高的狀況下,能夠犧牲一下性能,採用悲觀鎖。這個兩種方式的前提是採用MySQL的解決方案。併發