MySQL(表鎖)、PHP(文件鎖)鎖機制及應用場景


模擬高並發訪問一個腳本:apache安裝文件的bin/ab.exe可以模擬並發量

C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php   // -c 模擬多少並發量 -n 一共請求多少次 http://請求的腳本

Mysql中的鎖語法:
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【鎖表】
UNLOCK TABLES  【釋放表】

Read:讀鎖|共享鎖 : 所有的客戶端只能讀這個表不能寫這個表
Write:寫鎖|排它鎖: 所有當前鎖定客戶端可以操作這個表,其他客戶端只能阻塞
注意:在鎖表的過程中只能操作被鎖定的表,如果要操作其他表,必須把所有要操作的表都鎖定起來!

PHP中的文件鎖 :
文件鎖的文件與表有什么關系?:一點關系也沒有,與令牌相似,誰拿到誰操作。所以表根本沒鎖。
測試時,有個文件就行,叫什么名無所謂

總結:
項目中應該只使用PHP中的文件鎖,盡量避免鎖表,因為如果表被鎖定了,那么整個網站中所有和這個表相關的功能都被拖慢了(例如:前台很多用戶一直下訂單,商品表mysql鎖表,其他與商品表相關的操作一直處於阻塞狀態【讀不出來商品表】,因為一個功能把整個網站速度拖慢)。

比如在一個O2O外賣項目中,中午12-2點,晚上6點都是訂單高並發時,這種情況下,MySQL鎖顯然是不考慮的,用戶體驗太差。其實根據實際的需求,外賣可以不用設計庫存量的,當然除了秒殺活動模塊還是需要php文件鎖的。

應用場景:
1. 高並發下單時,減庫存量時要加鎖
2. 高並發搶單、搶票時要使用

Mysql鎖示例:

   /*
    模擬秒殺活動-- 商品100件
    CREATE TABLE ta
    (
        id int comment '模擬100件活動商品的數量'
    );
    INSERT INTO ta VALUES(100);
    模仿:以10的並發量訪問這個腳本!    使用apache自帶的ab.exe軟件
     */ 
    
    // 關閉錯誤報告
     error_reporting(0); 

     $dbhost = 'localhost:3306';  // mysql服務器主機地址
    $dbuser = 'root';            // mysql用戶名
    $dbpass = 'root';          // mysql用戶名密碼
    $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
    if(! $conn )
    {
        die('連接失敗: ' . mysqli_error($conn));
    }
    // 設置編碼,防止中文亂碼
    mysqli_query($conn , "set names utf8");
    mysqli_select_db( $conn, 'temp' ); 

    # mysql 鎖 
    mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一個客戶端可以鎖定表,其他客戶端阻塞在這 
    $rs = mysqli_query($conn , 'SELECT id FROM a'); 
    $id = mysqli_result($rs, 0, 0); 
    if($id > 0) 
    { 
        --$id; 
        mysqli_query($conn , 'UPDATE a SET id='.$id); 
    } 

    # mysql 解鎖 
    mysqli_query($conn , 'UNLOCK TABLES');
    //查詢解鎖后的id值
    // $res = mysqli_query($conn , 'SELECT id FROM ta');
    // while($row = mysqli_fetch_assoc($res))
    // {
    //     $id = $row['id'];
    // }
    // echo $id;

PHP文件鎖示例:

  /*
    模擬秒殺活動-- 商品100件
    CREATE TABLE ta
    (
        id int comment '模擬100件活動商品的數量'
    );
    INSERT INTO ta VALUES(100);
    模仿:以10的並發量訪問這個腳本!    使用apache自帶的ab.exe軟件
     */ 
    
    // 關閉錯誤報告
     error_reporting(0); 

     $dbhost = 'localhost:3306';  // mysql服務器主機地址
    $dbuser = 'root';            // mysql用戶名
    $dbpass = 'root';          // mysql用戶名密碼
    $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
    if(! $conn )
    {
        die('連接失敗: ' . mysqli_error($conn));
    }
    // 設置編碼,防止中文亂碼
    mysqli_query($conn , "set names utf8");
    mysqli_select_db( $conn, 'temp' ); 

    # php中的文件鎖 
    $fp = fopen('./a.lock', 'r'); // php的文件鎖和表沒關系,隨便一個文件即可 
    flock($fp, LOCK_EX);// 排他鎖 

    $retval = mysqli_query($conn ,'SELECT id FROM ta'); 
    while($row = mysqli_fetch_assoc($retval))
    {
        $id =  $row['id'];
    }
    
    if($id > 0) 
    { 
        --$id; 
        mysqli_query($conn ,'UPDATE ta SET id='.$id); 
    } 
    # php的文件鎖,釋放鎖 
    flock($fp, LOCK_UN); 
    fclose($fp);

    // $res = mysqli_query($conn , 'SELECT id FROM ta');
    // while($row = mysqli_fetch_assoc($res))
    // {
    //     $id = $row['id'];
    // }
    // echo $id;

搶券活動實例:

public function envelopeSnatching(){
        $lingqu = $_POST['type'];
        $uid=session('u_id');//用戶id
        if(!$uid){
            $data['msg']='您沒登錄,請先登錄!';
        }else if(date('Y-m-d') != '2017-12-12'){
            $data['msg']='不在活動時間內!';
        }else{
            $hours=date('H');//當前小時數
            if($hours > '09' || $hours > '17'){

                if($lingqu == 1 || $lingqu ==2){//點擊10點的
                    if($lingqu == 1){
                        $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判斷是否領取過
                        if($hours > '09'){
                            $num=mt_rand(25,28);//優惠券金額
                            $id=1;
                        }
                    }else if($lingqu == 2){
                        $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
                        if($hours > '17'){
                            $num=mt_rand(50,55);//優惠券金額
                            $id=2;
                        }
                    }
                    if(!$id){
                        $data['msg']='時間還沒到,晚點再來吧。';
                    }else{
                        if($is_lingqu){
                            $data['msg']='你已經領取過了,留點給別人吧!';
                        }else{
                            //鎖表
                            M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
                            $active=M('active_num')->where(array('id'=>$id))->find();
                            if($active > 0){
                                //開啟事務
                                M()->execute('start transaction');
                                $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
                                $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
                                $members_preferential    =    M('members_preferential');
                                //對應投資金額,
                                $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
                                $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
                                $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//過期時間
                                $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加優惠券
                                if($save && $add && $add2){
                                    //事務提交
                                    M()->execute('commit');
                                    $data['msg']='恭喜你領取了'.$num.'元優惠券';
                                }else{
                                    //回滾
                                    M()->execute('rollback');
                                    $data['msg']='未知錯誤!';
                                }
                            }else{
                                $data['msg']='紅包已領完,你來晚了!';
                            }
                            M()->execute('UNLOCK TABLES');
                        }
                    }
                }else{
                    $data['msg']='非法操作!';
                }
            }else{
                $data['msg']='還沒有到活動時間,請晚點再來喲!!';
            }
        }
        exit(json_encode($data));
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM