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://请求的脚本
复制代码

7 K- R+ b$ I/ E8 V) J, K
Mysql中的锁语法:
7 E8 I* R2 f; \% j" ~; MLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】) B$ [, I+ r, {/ `* _
UNLOCK TABLES  【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表+ ^& }; L3 Z9 b6 S. \3 ?, ?
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞3 B9 v0 t  Q- I; F
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :* t1 S' w* D' ]* @9 A' e& T; |
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
6 D% O( x) L& o* D3 U测试时,有个文件就行,叫什么名无所谓
总结:
* {; s0 ?: B) [! ~8 s  B项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:  s/ E1 I; ~) Z8 }3 P/ x+ v
1. 高并发下单时,减库存量时要加锁
  c8 U2 D/ Q" R( n: P# [/ p2. 高并发抢单、抢票时要使用
Mysql锁示例:
  1.    /*
    - @7 q) B- R6 ?1 F* f- y1 I
  2.     模拟秒杀活动-- 商品100件+ v# D8 s* S7 g5 n" U  B
  3.     CREATE TABLE ta
    6 _6 ?3 h) |: J4 A( I8 p
  4.     (7 T  u/ }+ Y) J) P! _7 F; k/ |
  5.         id int comment '模拟100件活动商品的数量'
    ! z. s, U( C' G: R9 q3 U* C
  6.     );& |9 W1 y* Q& x# I; [  ?9 b. ]9 x
  7.     INSERT INTO ta VALUES(100);: P# f8 o8 }; R1 j0 R
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件
    ( P* u, v6 Y- l; a' K
  9.      */
    6 J# _: J3 j- ]$ a6 i
  10.     , ^+ {- t3 K5 i, `8 a; X
  11.     // 关闭错误报告' v0 g. a9 W/ ~; z% a
  12.      error_reporting(0); ( U, S. a; W5 J& e1 a

  13. % ^* I6 k+ V0 W7 w& G+ R* _
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址
    " }1 X" n3 s$ M7 V8 U
  15.     $dbuser = 'root';            // mysql用户名: @  l# o+ p! k
  16.     $dbpass = 'root';          // mysql用户名密码: Z% z7 z3 f. z
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);8 e1 G0 E6 P- z+ ]  Z9 u! S
  18.     if(! $conn ): B4 ]/ `& `% l# C" o2 p
  19.     {
    . W- t2 V! V1 `  ^, g) S# h* @( `
  20.         die('连接失败: ' . mysqli_error($conn));/ N/ a% n6 n. e/ `
  21.     }
      R) Z8 S. Q1 h' L
  22.     // 设置编码,防止中文乱码3 O8 X- }/ S# `7 r
  23.     mysqli_query($conn , "set names utf8");7 H3 D. |4 |7 V6 @0 K
  24.     mysqli_select_db( $conn, 'temp' ); 3 p7 f& e3 H! `, N  P

  25. 8 V' b$ A* t/ ^# K& Y) S) c( z
  26.     # mysql 锁 / w7 r) L8 t6 x+ I" b6 U
  27.     mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
    " k0 Q2 n) H0 M" i% l
  28.     $rs = mysqli_query($conn , 'SELECT id FROM a'); 5 q9 ^( O* K9 l2 \& {5 l) L+ \
  29.     $id = mysqli_result($rs, 0, 0);
    8 l; a; N, d' Z9 _
  30.     if($id > 0) , Z) e1 D6 o* z6 c
  31.     { 7 Y3 _2 y! X, S9 X7 D, A
  32.         --$id; ' P( W  R( S/ a6 R- M' H
  33.         mysqli_query($conn , 'UPDATE a SET id='.$id); , `9 A, ^) b* v2 `0 L2 U
  34.     }
    5 F* M# E0 |" D8 ~

  35. % |# Z/ T( Z- h- t$ y
  36.     # mysql 解锁
    / T7 \! f- u" p  U
  37.     mysqli_query($conn , 'UNLOCK TABLES');8 d$ w, L0 x! k: f2 R! t- M8 b' p
  38.     //查询解锁后的id值
    # C2 a$ S1 k/ ^" a; Y% F, B7 d* T
  39.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    ( |$ M1 i8 r, ~) {% l- k8 g
  40.     // while($row = mysqli_fetch_assoc($res))/ Z0 ?$ q" d6 @3 w3 c- f3 m
  41.     // {, z8 z" j$ O# `* A
  42.     //     $id = $row['id'];# B! D- }8 Y6 D. c) c5 S4 F
  43.     // }
    - g$ L$ p0 k( d2 ]7 h- ]
  44.     // echo $id;
复制代码
- n; a+ P2 j- P9 W& ~$ C  |# E% j
6 J3 z  ?( u+ V7 r6 m( l
: L; c- s" I; T3 ?' ~* B/ k
PHP文件锁示例:
  1. /*
    $ g  L5 \6 [( L9 h- x2 M9 n
  2.     模拟秒杀活动-- 商品100件  O2 t* h8 ^* {7 B5 j7 N- S  z# X
  3.     CREATE TABLE ta
    ! X! P8 G8 M+ o' B* A
  4.     (( |, I/ F' X: o, ~( S  W3 R/ R
  5.         id int comment '模拟100件活动商品的数量'
    % T& N$ R! i& {( T
  6.     );/ F6 R) G/ h0 w6 C2 W) f1 M5 N
  7.     INSERT INTO ta VALUES(100);, L% @1 q) G# u( n
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件
    9 F, d% `4 r* h- G- u1 |: k1 `
  9.      */ 1 S  M) t9 [% D+ B& ^7 b. h
  10.     % K6 r, b- n9 D& G  [. v+ i) r2 r
  11.     // 关闭错误报告
    " p* n1 s: V! g. v
  12.      error_reporting(0);
    5 Y) W$ |) S) K' x9 V

  13. 8 f7 k, L- O5 A9 j5 U
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址! ]5 X8 j: |" ~# U0 Q$ M
  15.     $dbuser = 'root';            // mysql用户名- i# E+ a9 ?9 X9 D
  16.     $dbpass = 'root';          // mysql用户名密码
    % o( s$ d. H2 q* D( I+ J+ C' X
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
    ! V, D5 V5 K1 n# B
  18.     if(! $conn )' C  }4 Q; _; g+ H& X
  19.     {8 D* t7 U8 C6 O9 [# ?/ H$ c5 Q, N7 p
  20.         die('连接失败: ' . mysqli_error($conn));
    / T8 V; @- _2 S4 P1 F7 q9 a
  21.     }
    ! |8 k) t3 L' E7 j
  22.     // 设置编码,防止中文乱码
    - y$ P. ?+ u+ E* \4 Z% D1 o2 a
  23.     mysqli_query($conn , "set names utf8");' l& }+ F) c7 q$ q( y! b  x1 e
  24.     mysqli_select_db( $conn, 'temp' );
    ) S( y( f+ N' x, o: d
  25. 4 p$ u2 l* x" B# k+ o# T- D- `9 j
  26.     # php中的文件锁
    ' ]& v* o: d1 j" e( _
  27.     $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
    - o2 w: N, X8 a, F% `/ A# ^/ M
  28.     flock($fp, LOCK_EX);// 排他锁
    / X) Y! j$ ?/ z7 `2 ]4 S

  29. 3 `6 ?# h5 |8 f/ p5 u7 c3 ]
  30.     $retval = mysqli_query($conn ,'SELECT id FROM ta'); / I+ Z2 [9 y1 [
  31.     while($row = mysqli_fetch_assoc($retval))4 s  N2 o% g% Z, s2 h8 U
  32.     {% k' ]6 e; U' Z. f; w
  33.         $id =  $row['id'];
    9 I, c$ f& |% k" P7 Y
  34.     }, @: r, @7 |$ ]; b% O8 z  l
  35.    
    4 i- E: O" ]" z* }$ t$ v  \3 ^
  36.     if($id > 0)
    & z8 G: u; T; T
  37.     {
    ; j! @  y1 Q4 F) u8 j3 p3 e2 U
  38.         --$id; ' J1 t' m& W) s  K( l( j% @
  39.         mysqli_query($conn ,'UPDATE ta SET id='.$id); & g# V- W4 ^5 V) ^
  40.     }
    6 h, n0 t2 E' A" @" f: B
  41.     # php的文件锁,释放锁 ! |8 M: K; k) ]- K  f
  42.     flock($fp, LOCK_UN);
    / ]. h3 ^. k8 d
  43.     fclose($fp);
    * T3 K3 z% b  b' p3 x4 ~4 K4 J

  44. 9 K7 ^& x8 v) o, \" ~- L- Z
  45.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    , p  o! ]9 A) _# q
  46.     // while($row = mysqli_fetch_assoc($res))
    1 q/ c, M1 C- k2 W/ ~( }  s$ D8 q. E
  47.     // {" f5 t3 E( g# T3 ^
  48.     //     $id = $row['id'];
    8 _  l& ^2 R% i$ b: e
  49.     // }
    3 Q- r: D+ |( t. D
  50.     // echo $id;
复制代码

+ e- k7 t( x7 ?8 o) w6 K" w9 |: Q- M7 ^  Q
抢券活动实例:
  1. public function envelopeSnatching(){
    ! t# E( {1 N1 G8 r7 N
  2.         $lingqu = $_POST['type'];
    1 o! Z( j* |+ ~+ _
  3.         $uid=session('u_id');//用户id8 n  L" p2 @  i1 I3 h
  4.         if(!$uid){
    ! r/ V' W* B9 p; y% {# n
  5.             $data['msg']='您没登录,请先登录!';9 h* l: U& A6 M- n% D8 Q
  6.         }else if(date('Y-m-d') != '2017-12-12'){* Y" o7 P: z9 M2 }. \3 F
  7.             $data['msg']='不在活动时间内!';
    4 L  N! p& E4 \) y
  8.         }else{, ?+ r$ [! s, k8 n5 |
  9.             $hours=date('H');//当前小时数/ b; B* {+ O7 V
  10.             if($hours > '09' || $hours > '17'){: U# ^- F9 e/ b; Y) a- W

  11. ! |8 f4 G; N/ D' |$ c
  12.                 if($lingqu == 1 || $lingqu ==2){//点击10点的
    ( W! J" ~2 k9 m9 c8 j2 W
  13.                     if($lingqu == 1){: B- m  M' b# o# N! e
  14.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
    ) b$ w7 q3 p! H( I+ V, J
  15.                         if($hours > '09'){
    % P, p  {% K$ y( k* S6 s3 d
  16.                             $num=mt_rand(25,28);//优惠券金额# c/ W6 t: N& ?# Y
  17.                             $id=1;
    " S# v! W0 o" e
  18.                         }
    ' m2 g8 [( R- e
  19.                     }else if($lingqu == 2){
    / b9 v8 f* I  n" z* x. X
  20.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
    3 A, t! P& J2 y( l; ]1 l
  21.                         if($hours > '17'){
    % _, ?2 M& J: P) G; {. ~) V
  22.                             $num=mt_rand(50,55);//优惠券金额
    3 u% }3 Q& {! w3 a
  23.                             $id=2;
    6 K& b$ L: m( ~) f% `3 a
  24.                         }$ b0 Z- }6 {+ L2 B" g4 `
  25.                     }
    ( U' S" ]5 U# C6 N; p8 k0 j" M$ y
  26.                     if(!$id){8 k- o/ `5 W6 P- }
  27.                         $data['msg']='时间还没到,晚点再来吧。';1 U/ B1 f3 B; ^! a
  28.                     }else{: [5 ^: U( {! T* G) t9 _- X( T
  29.                         if($is_lingqu){* e9 H' H5 v8 ?. k0 P& E- D
  30.                             $data['msg']='你已经领取过了,留点给别人吧!';4 j6 Q/ ~: C+ y' {9 z+ f6 y
  31.                         }else{
    ! c# f1 _: F* X+ z9 i1 e0 F  U
  32.                             //锁表
    0 R1 v) V) ~0 L2 E5 O  K0 B" o
  33.                             M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
    ( a9 N& X* u" x. k
  34.                             $active=M('active_num')->where(array('id'=>$id))->find();
      ?% B2 N8 U& \  X. |5 ]; \2 I
  35.                             if($active > 0){) P. b* x; B( m1 C
  36.                                 //开启事务
    / `, \& T# r4 H" [
  37.                                 M()->execute('start transaction');
    & u% a. h, ?' f: X6 k
  38.                                 $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));8 T3 `9 S+ g1 g& S
  39.                                 $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
    ; o% ?! n; K0 d, _
  40.                                 $members_preferential    =    M('members_preferential');
    # J# Y4 d% @" g7 f( P, }
  41.                                 //对应投资金额,$ n# h, M' ]0 D8 Z
  42.                                 $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));& z2 H( b  n1 X) ?9 V
  43.                                 $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');0 z3 Q" o5 i6 }2 U% d3 s+ a( {
  44.                                 $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间) s9 h7 `8 ^/ [' C% H) N, I3 L
  45.                                 $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券" k( V. N, d2 A' j
  46.                                 if($save && $add && $add2){
    . }$ G& g3 k4 s/ e$ ^9 K
  47.                                     //事务提交
    % f# `4 ~" G; }3 d& b" [  m) y
  48.                                     M()->execute('commit');) y9 I$ w- z- h; b* F" G+ v
  49.                                     $data['msg']='恭喜你领取了'.$num.'元优惠券';0 f! O9 R0 s7 R8 Z3 f
  50.                                 }else{
    ( p5 {7 k+ W6 `8 o
  51.                                     //回滚
    ( S7 P, H# r% h8 i) `
  52.                                     M()->execute('rollback');& |& V  J5 c( T# \( s- s
  53.                                     $data['msg']='未知错误!';
    3 D- y$ ]9 w4 ~
  54.                                 }
    ! E6 Q) u9 x9 w3 O" Z% f( j
  55.                             }else{/ p5 W* p, }4 m6 x. J0 d3 i
  56.                                 $data['msg']='红包已领完,你来晚了!';
    - R' J+ B6 M$ U, ^4 {
  57.                             }- U- g- Z6 o% E7 ], T
  58.                             M()->execute('UNLOCK TABLES');
      N& u. I; s2 o; L0 P
  59.                         }, \7 {, G  T3 M' {2 ?
  60.                     }6 I5 H% b9 m2 D3 {, U6 Y. {
  61.                 }else{
    - o: b2 e4 l, n5 c) x, A+ G
  62.                     $data['msg']='非法操作!';
    " w7 o% i# [. V
  63.                 }3 ~! ?3 o2 }0 d: W
  64.             }else{
    ( ~* t4 s# e, v; L1 {
  65.                 $data['msg']='还没有到活动时间,请晚点再来哟!!';6 e* u4 Q* [6 \: h$ T/ L; }+ b
  66.             }
    ! R' f4 \4 {- v* P
  67.         }
    7 p$ P3 F. b' l- s9 M
  68.         exit(json_encode($data));  b& M8 w" g  O! k/ b& n6 u
  69.     }
复制代码

- C: U) [  I4 A/ \0 q+ B: J1 J3 Q* V





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