cncml手绘网

标题: MySQL(表锁)、PHP(文件锁)锁机制及应用场景 [打印本页]

作者: admin    时间: 2022-3-17 15:53
标题: MySQL(表锁)、PHP(文件锁)锁机制及应用场景
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量
  1. C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php   // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
2 \7 t4 Z* J! p7 o7 I
Mysql中的锁语法:
" M1 T3 |) {+ F! ]1 U/ t1 BLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】" h3 g% C3 Q; [6 |+ h
UNLOCK TABLES  【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
! ?; X/ D, n& s% z! O2 }' r9 e) tWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
6 r, z( b4 ~$ y注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :
! z; |' ^) B  t2 n3 e1 A, i文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
$ W9 K: K- {3 F) A( k测试时,有个文件就行,叫什么名无所谓
总结:2 a; m' i( W! Q, m5 p
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:" d" ]( _7 H7 e# z- n$ o# y
1. 高并发下单时,减库存量时要加锁
8 D9 B4 {1 o3 v) B8 O/ r2. 高并发抢单、抢票时要使用
Mysql锁示例:
  1.    /*: d7 }/ r* K1 J' Y0 F$ {$ h; Y
  2.     模拟秒杀活动-- 商品100件
    & d" N0 X6 P  H9 w/ |
  3.     CREATE TABLE ta
    1 R$ U3 [5 _/ g& r) m) Q) ^
  4.     (/ Q$ K. d% v7 t& H+ |
  5.         id int comment '模拟100件活动商品的数量'4 a5 L( B1 r2 E2 N! Y" J
  6.     );
    , Q% k6 I9 r( n
  7.     INSERT INTO ta VALUES(100);, Z8 U) k8 f1 [( y& i" e  L; d
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件
    8 ^8 X3 Q% c/ T+ J
  9.      */ + J: [8 C2 M: |6 w+ R- Q
  10.    
    9 ?1 k+ \: W% v
  11.     // 关闭错误报告
    ' u9 E' \* O3 f1 h9 I# b1 q. I
  12.      error_reporting(0);
    9 }7 K- d0 k( H* ^/ J  N, w6 R
  13. , `5 b* R0 I; _6 z
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址
    , P- H  `# {( B0 l+ H# Q3 F
  15.     $dbuser = 'root';            // mysql用户名, h; ~# f6 W( b
  16.     $dbpass = 'root';          // mysql用户名密码
    * ?/ }( B# h$ t% U5 y: l
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);7 f7 U( D' c! L( F$ \
  18.     if(! $conn )7 _: K# ]8 M0 @' O
  19.     {
    + ^9 |4 t6 b, T% z9 `0 _% d
  20.         die('连接失败: ' . mysqli_error($conn));$ n2 t  B, p1 S
  21.     }
    8 ^- A; y: M8 y: G  ~2 e
  22.     // 设置编码,防止中文乱码
    0 e% V) O, ?3 H: F, i
  23.     mysqli_query($conn , "set names utf8");9 g, w) N6 e' O% A5 w
  24.     mysqli_select_db( $conn, 'temp' ); : M8 c! A; R5 v" o7 w
  25. ! L. J3 E+ s: k% {$ V# m9 k
  26.     # mysql 锁 0 N( a6 t# e- r
  27.     mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
    ( U4 {3 C7 F, l) {5 n
  28.     $rs = mysqli_query($conn , 'SELECT id FROM a');
    7 m) ?* @7 X9 D" ~2 x% Y* R
  29.     $id = mysqli_result($rs, 0, 0); ' F1 Q4 D6 M& `; p- r; x
  30.     if($id > 0) 4 A2 A: |( |- u, Q" I3 ?1 k2 q
  31.     {
    " a1 |2 ], t1 w9 W. {7 g
  32.         --$id;
    ) S  J( P# f9 }! |
  33.         mysqli_query($conn , 'UPDATE a SET id='.$id); 8 o5 V$ \& ~6 {( X/ L
  34.     }
    ; m/ _3 \% s: n2 O  ?1 c
  35. ; a4 J- ]" ?3 ?7 }5 [: \' g
  36.     # mysql 解锁 & A8 s# Q9 @" o# z+ ]
  37.     mysqli_query($conn , 'UNLOCK TABLES');! {* {  O4 W% {4 l) l; \
  38.     //查询解锁后的id值
    ; s( f$ c- q2 J4 L, R# _  P6 ~
  39.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    ( r! G) e" E% H! Q6 H
  40.     // while($row = mysqli_fetch_assoc($res))6 A/ R0 v: h# Q7 e4 Z, r2 m
  41.     // {
    . k1 o* e, a( t& R' I5 x0 o# Q' ?
  42.     //     $id = $row['id'];
    ) D! g# T+ V2 s$ @5 S& F# W( h5 n$ {6 V
  43.     // }1 x1 Y5 V/ A) }, T6 h( m
  44.     // echo $id;
复制代码

- t1 r" ?9 G- G  n5 x8 c
7 n* J8 W( X) `" ~
. @# x  _6 L* J5 q& v) d
PHP文件锁示例:
  1. /*. {$ e2 g1 z4 y1 x; r
  2.     模拟秒杀活动-- 商品100件3 ]) u( E+ D+ ^' D/ {
  3.     CREATE TABLE ta' R' D* ~: @; D: f5 \
  4.     (
    ( O; \3 u+ l6 `- `0 F8 E, {. v
  5.         id int comment '模拟100件活动商品的数量'$ T/ N) p- K' p' a. l: ^
  6.     );5 j3 ?8 i/ ]4 k0 H
  7.     INSERT INTO ta VALUES(100);, ]( p% ~8 _0 N' j/ X
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件
    ) C9 T7 Q) J% i; p( R1 ]
  9.      */
    . C$ a* V( I$ N
  10.     6 I# P9 F  Q6 P- w' ]
  11.     // 关闭错误报告' b" Y0 L; l9 g; i7 r0 l
  12.      error_reporting(0);
    ; @) P9 ?4 n" i% v, i5 U7 D
  13. ) m# D2 [% f0 e
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址- [( f6 _; c/ X7 z. v* I* w* M+ x
  15.     $dbuser = 'root';            // mysql用户名! ~5 h7 D* R1 h. a
  16.     $dbpass = 'root';          // mysql用户名密码
    . ?+ W. g: J/ D: n- E0 N( C1 p( W
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
    $ d' b. Y: C: o- O; m" S* u6 ?
  18.     if(! $conn )
    # D" ]2 K+ P0 K9 d
  19.     {( u& a/ L/ ?- c5 h$ j. `% o
  20.         die('连接失败: ' . mysqli_error($conn));; |6 S' q& z- o1 ~1 }
  21.     }
    1 j7 r1 K7 R' G. k. U  i
  22.     // 设置编码,防止中文乱码
    4 n. j  T+ T" @6 a+ z# L
  23.     mysqli_query($conn , "set names utf8");- N# S" F6 c) C! O: H2 p
  24.     mysqli_select_db( $conn, 'temp' ); 3 L  P) G: j" {: V) F  P! i

  25. 6 M8 d; d( S$ H' u1 ^
  26.     # php中的文件锁
    + Y$ r' X2 ]/ m) U% Z
  27.     $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 . C5 k+ N8 T1 a9 V$ B5 S+ F/ Y5 J6 H7 |
  28.     flock($fp, LOCK_EX);// 排他锁
    / q8 g# Z3 p& _9 P

  29. $ T8 j$ d8 _# y1 e/ @2 g
  30.     $retval = mysqli_query($conn ,'SELECT id FROM ta'); ; L1 h& d' P5 L& w# }- K
  31.     while($row = mysqli_fetch_assoc($retval)); X: O' [+ Q1 ?" |* H
  32.     {
    - y3 d  C' q% k$ d. N1 a7 d$ K
  33.         $id =  $row['id'];' g( L- e5 s+ S( {6 A( O
  34.     }# q; G5 p# D) d/ o! d# b) f& S. {) o
  35.    
    . X# t1 R! ~3 O5 s5 F. e2 T0 L) y! c
  36.     if($id > 0) + ?* }% f0 B, k& V/ V/ d
  37.     {
    3 K) u. U. p8 h
  38.         --$id; ) ?3 Q' e! f" D0 {) C7 R
  39.         mysqli_query($conn ,'UPDATE ta SET id='.$id); ( p- i, `* }( l8 M
  40.     } + e$ n5 L8 L( l
  41.     # php的文件锁,释放锁 ' a+ z" p2 ^/ |5 V2 ~$ c- {$ G
  42.     flock($fp, LOCK_UN);
    # u  J+ ]) w/ p' C
  43.     fclose($fp);
    ! V: h) Y% I" n6 v5 i

  44. ) z  z" @+ p  }) S; I- g/ D
  45.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    ; q( p; |7 ?, _2 Y1 m
  46.     // while($row = mysqli_fetch_assoc($res)): ~7 v4 b6 S* M3 q4 h. r+ S
  47.     // {
    % a8 T( s! z/ D7 @- y: w% g
  48.     //     $id = $row['id'];
    / u% s" J% _* d5 L$ Q( y5 y
  49.     // }! \$ y5 p7 h8 b) ~4 v7 R% f' O
  50.     // echo $id;
复制代码
  n/ I- P) C9 M" t8 p0 c- `$ w
9 y& U8 |2 t) {& x
抢券活动实例:
  1. public function envelopeSnatching(){0 X2 ~, w; y" b5 D, [  P! ?
  2.         $lingqu = $_POST['type'];
    3 Q& O3 M2 ]' s. g4 [
  3.         $uid=session('u_id');//用户id+ Q. X! @, O1 k8 ]1 Y
  4.         if(!$uid){* l: g" n1 _. V# }2 d( C
  5.             $data['msg']='您没登录,请先登录!';
    + `' q4 F1 j* J+ c$ d
  6.         }else if(date('Y-m-d') != '2017-12-12'){6 r+ y7 Y8 w6 A7 M7 A; \
  7.             $data['msg']='不在活动时间内!';- X- n" T% H+ P( Y6 G5 z
  8.         }else{/ ~0 E, t% P# m( ?" [1 e
  9.             $hours=date('H');//当前小时数
    / Q% @  {4 L" m1 Z* @
  10.             if($hours > '09' || $hours > '17'){
    / n/ U, r* |4 N' ?* u3 ?
  11. 0 {3 K* A  K! G% [3 R; L0 x+ E
  12.                 if($lingqu == 1 || $lingqu ==2){//点击10点的
    ! |6 A6 n) J* @/ r5 F
  13.                     if($lingqu == 1){
    : N6 F# J6 h+ z' n& v
  14.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过: g& x" B, a) q1 Y1 R- |+ g( N4 y
  15.                         if($hours > '09'){
    2 ?# s. h. w1 `; O0 ~& d1 ?
  16.                             $num=mt_rand(25,28);//优惠券金额- Q( F5 b# }6 Q8 v! D2 I. d
  17.                             $id=1;
    3 Y/ K" u' v9 e0 u8 Y5 t2 L
  18.                         }
    6 J+ J: I5 k9 o# P+ s
  19.                     }else if($lingqu == 2){# J) b. h+ x/ }; X9 M" w
  20.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
    ) c0 H" `' i3 E. B: l1 c; m
  21.                         if($hours > '17'){
    % F2 P% I2 W7 F% X$ V" U
  22.                             $num=mt_rand(50,55);//优惠券金额
    : ?" O: D6 B+ h% c, z7 y( J
  23.                             $id=2;
    ' P) h4 S! W' ]( I
  24.                         }8 I  D6 J3 m- u4 d- Z
  25.                     }8 |3 \' I& Z3 h5 J
  26.                     if(!$id){
    # M0 c4 X6 `2 f/ j' V1 H4 d. n
  27.                         $data['msg']='时间还没到,晚点再来吧。';/ [$ Q) Z; ~' v9 A2 G
  28.                     }else{
    8 R8 M3 h, v% U0 ?, u, c  P* ~
  29.                         if($is_lingqu){
    3 `( X8 n! L6 l, ~0 i$ t, c
  30.                             $data['msg']='你已经领取过了,留点给别人吧!';
    & }, |1 c. f0 v( K
  31.                         }else{
    . w! j. f4 c# P. R
  32.                             //锁表; Q; W4 E, I. I3 a7 z8 N% X) A
  33.                             M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');% `# o3 @) _9 U9 z( E4 n' {- {
  34.                             $active=M('active_num')->where(array('id'=>$id))->find();
      k( q! M6 B7 ]7 V4 n' i" F1 h. i6 `
  35.                             if($active > 0){
    * O( r* U' h- a
  36.                                 //开启事务) ^6 _4 X. b% j% h+ y
  37.                                 M()->execute('start transaction');9 N6 x3 I- n3 B4 X7 ^
  38.                                 $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
    5 X6 `, M! B) i1 S4 @3 C6 c2 J6 A2 F
  39.                                 $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
    8 Z5 K8 Z: ^8 b6 A( q
  40.                                 $members_preferential    =    M('members_preferential');
      v2 a* h  P2 H2 u
  41.                                 //对应投资金额," P( [- q9 d% O5 a
  42.                                 $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
    9 e; D) q7 \$ O( ~# X
  43.                                 $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
    3 p9 j# q( L4 _3 m
  44.                                 $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间! ~, w- P- g+ p% \2 j; o" O( A
  45.                                 $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券" r6 O2 N0 C% d% Y7 y6 ?& h/ W
  46.                                 if($save && $add && $add2){3 [- y4 o' ?% n7 P( Z$ s
  47.                                     //事务提交$ r( b7 t* `. v- G" c
  48.                                     M()->execute('commit');( ]% O$ h7 G" f9 k! ^  _% A8 e* t7 [
  49.                                     $data['msg']='恭喜你领取了'.$num.'元优惠券';9 q" X; p1 P1 K) w2 s1 \( l
  50.                                 }else{
    ' m5 e. V8 D4 d0 ]# W! u9 d
  51.                                     //回滚; H6 Z) N& A, P# Y3 h* W
  52.                                     M()->execute('rollback');; r/ a0 X" y- k2 y0 W) }& K, Z6 p
  53.                                     $data['msg']='未知错误!';+ M  y( `4 Y0 `8 e9 ]! _
  54.                                 }( @4 }5 q7 M0 ?4 y1 `5 \; a5 o& B
  55.                             }else{
    ; m  ?( E3 D! u7 G
  56.                                 $data['msg']='红包已领完,你来晚了!';
    " f4 h8 I; B' T. |- e& n; a
  57.                             }
    - d; `$ F$ r# \: h( n
  58.                             M()->execute('UNLOCK TABLES');
    0 o7 s" Y6 @+ _% D/ z
  59.                         }
    " c" Q8 x: ^# E( A
  60.                     }
    ( A4 e* o5 I) x+ K% }
  61.                 }else{
    ; [4 b/ M, ~* h0 ^+ ?. C
  62.                     $data['msg']='非法操作!';9 u3 d- k$ z, a9 y8 |1 l
  63.                 }
    * n( ]+ C3 |5 ?: v" F" {- F
  64.             }else{$ q2 e+ z/ }% E, P! {' k8 E  g
  65.                 $data['msg']='还没有到活动时间,请晚点再来哟!!';
    9 u! ~% I. W! T0 l: V
  66.             }
    1 b' y7 j) L4 ]3 T9 Z, @7 Z5 k
  67.         }1 e( s, O* i& |+ w' ^
  68.         exit(json_encode($data));
    4 m; \# `( r' n8 Z* E. ~
  69.     }
复制代码

- J4 N# C7 I; m# E3 z; E6 y3 ^2 E! z. g9 T& K3 E, I5 M! f





欢迎光临 cncml手绘网 (http://bbs.cncml.com/) Powered by Discuz! X3.2