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://请求的脚本
复制代码
+ f1 v! F4 U* u' Z5 @& I, Q" o
Mysql中的锁语法:
% D* F$ D& S1 c7 f8 X' |2 bLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】2 R7 J( Q( i9 o7 H0 m4 z
UNLOCK TABLES  【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表, v; v( a- o4 Q. m' j2 Q
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞; M# f9 h5 Z+ a
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :
. v1 x) X6 i& ?1 s: g/ o1 o# o& ~文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
6 U* `( Z% m$ t+ }1 w! t测试时,有个文件就行,叫什么名无所谓
总结:
& P, l* s; H" _8 G项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:
( q3 M4 l; v8 S: _8 x7 Y1. 高并发下单时,减库存量时要加锁+ W1 X2 a( L- k$ ~" h: w  [
2. 高并发抢单、抢票时要使用
Mysql锁示例:
  1.    /*
    , ^8 m3 D8 o9 h
  2.     模拟秒杀活动-- 商品100件( f, i& P& L$ a" m) e: ~0 o
  3.     CREATE TABLE ta. X* |' ?/ A8 B+ w9 f5 e9 f3 |
  4.     (
    3 x2 l9 c* S) z" }+ l7 r
  5.         id int comment '模拟100件活动商品的数量'7 v& t: ~. e2 j1 X
  6.     );7 ~, h" C) L" j/ q
  7.     INSERT INTO ta VALUES(100);" s% T' ^  N6 T8 K8 Z- R
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件* e7 z" Q+ E; c# G
  9.      */   V! z% g- g* ]+ M9 M
  10.    
    8 Q! z. v8 Q3 ^# c, T, H
  11.     // 关闭错误报告
    + P: b3 [2 o8 A5 [5 B# j2 }1 ]
  12.      error_reporting(0);
    5 T4 r8 t0 j+ g0 s
  13. ! F) R; q3 ?2 p& T
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址
    + E- K6 E& o/ J7 s# W
  15.     $dbuser = 'root';            // mysql用户名- Z& A/ N, ?$ }  f
  16.     $dbpass = 'root';          // mysql用户名密码
    + r  n$ I$ W, N5 H
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);9 m; }0 y1 _3 L- T& Q! |7 b/ n
  18.     if(! $conn )' ^- X+ r; z  f8 G9 b
  19.     {4 q- G2 U' w6 j" H
  20.         die('连接失败: ' . mysqli_error($conn));
    + F+ C' s. w/ O6 K6 D
  21.     }4 s5 T( C$ A" w% z6 M7 K
  22.     // 设置编码,防止中文乱码
    ) u. k1 W! V! k0 j; O1 K9 a' [
  23.     mysqli_query($conn , "set names utf8");3 }5 r* L) B2 g# i4 t( W9 X$ ]; B/ o2 u
  24.     mysqli_select_db( $conn, 'temp' );
    7 S- r4 Y% D$ V4 E0 S

  25. ' q, q5 H; ?9 O7 W+ h
  26.     # mysql 锁
    5 D+ n( X, ^! j& \5 H  |
  27.     mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
    . J8 b+ E# {, H0 [
  28.     $rs = mysqli_query($conn , 'SELECT id FROM a');
    1 |8 u2 ~; E: t+ R  E9 P
  29.     $id = mysqli_result($rs, 0, 0);
    5 A0 p  C, l* f6 u+ C2 u
  30.     if($id > 0) ) Z+ B0 a, L1 O
  31.     { % Q1 @- W- X! s$ L8 \) M
  32.         --$id;
    - Y3 I& G" E! F$ m" j4 v1 ^/ k
  33.         mysqli_query($conn , 'UPDATE a SET id='.$id); 4 t1 |/ F- u9 q* y
  34.     }
    , ^! j: T+ @& Y$ L

  35. , S8 q: a' F' ^2 l* I" `
  36.     # mysql 解锁 - O: B) a. b/ S1 h  f
  37.     mysqli_query($conn , 'UNLOCK TABLES');
    ( F" M, K# m" I; ?7 M
  38.     //查询解锁后的id值9 A* ^+ I- {" Z/ w- z* z9 C
  39.     // $res = mysqli_query($conn , 'SELECT id FROM ta');# D' F$ @- G8 d* o
  40.     // while($row = mysqli_fetch_assoc($res))
    1 m- y9 {2 \- m8 A
  41.     // {
    * A9 \: e. p" `* @7 L2 |( y9 O
  42.     //     $id = $row['id'];
    " ?9 {1 \' e; B8 J
  43.     // }
    - L1 H7 m- H: @
  44.     // echo $id;
复制代码

" x+ [+ V. H/ ], a. J' |% r2 o# c& U! V% b# m# t- g9 ]

# _3 Y% A9 c' N$ V' y; K) i' A# n! z
PHP文件锁示例:
  1. /*1 F' G, e$ N# a  j
  2.     模拟秒杀活动-- 商品100件$ u! S+ }' P1 X
  3.     CREATE TABLE ta! [- X( q& p( K9 S8 g
  4.     (
    # H: f  w/ \4 F* R) N
  5.         id int comment '模拟100件活动商品的数量'
    6 l7 J$ f4 Y0 B) Q8 Y
  6.     );
    0 b$ U2 e8 l( G% x4 v' ]
  7.     INSERT INTO ta VALUES(100);* Y+ `" c! l8 b" O
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件8 m; N; J6 ?/ L- q" f0 P7 }4 `
  9.      */ / [5 W# Z: x5 L$ Q0 \. G
  10.     / s& s% ^1 K4 k
  11.     // 关闭错误报告
    1 B0 _- T4 D! q# d
  12.      error_reporting(0);   |5 ^9 l; a- @, b1 ]
  13. - L5 \7 \- _; n9 v8 z& E* H
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址- l- j. `$ p( F, `9 b) I" @
  15.     $dbuser = 'root';            // mysql用户名
    ( G3 h6 p* U2 U. b6 i  ~
  16.     $dbpass = 'root';          // mysql用户名密码" p0 V  i! i1 Q
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
    + N# \5 w7 O) I1 r
  18.     if(! $conn )9 u  _/ v$ `# y% }8 d4 L
  19.     {
    5 i8 p, \9 J$ F6 P! n* |
  20.         die('连接失败: ' . mysqli_error($conn));1 @" F0 U. s% j) f+ r2 t
  21.     }
    : v% t4 D$ c3 k: ~
  22.     // 设置编码,防止中文乱码7 q. `7 b" J2 |! y/ W5 w
  23.     mysqli_query($conn , "set names utf8");
    0 z7 a/ L! {/ f& e( q1 G
  24.     mysqli_select_db( $conn, 'temp' );
    ! F$ k! Y0 `+ v' H, b
  25.   j! j; `- M3 {: M- Q
  26.     # php中的文件锁
    9 q4 H& ^# b9 O: h  `
  27.     $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 + x, W9 j3 J3 ?
  28.     flock($fp, LOCK_EX);// 排他锁
    + h* R% C* j( u

  29. 8 J3 {" F  u+ K& c; M3 K1 l
  30.     $retval = mysqli_query($conn ,'SELECT id FROM ta');
    ) j! w& N. k* h( m! u5 ^6 S
  31.     while($row = mysqli_fetch_assoc($retval))8 \0 C, W8 q7 w8 Y
  32.     {. a& |4 X. m4 p- W* f& b* B! R
  33.         $id =  $row['id'];
    # u2 U$ y7 Q* T& x( f! Q& w" p
  34.     }
    & O/ z( }9 z/ V. y1 e5 N' G
  35.    
    9 ^- i) o; f; C0 ]( Q5 r" }" `3 Q
  36.     if($id > 0)
    ; I& v9 E0 ?5 `! N* e* X) t+ l) E
  37.     { ) i8 z- q# z. F) [0 M
  38.         --$id;
    " T( U( a# b! f5 `$ J3 Z
  39.         mysqli_query($conn ,'UPDATE ta SET id='.$id);
    ; p# s: \" f& ^) P4 i5 S5 D/ n# X$ t/ \
  40.     }
    3 H: P6 P+ p0 l& O0 f3 k
  41.     # php的文件锁,释放锁 ( r( P0 [2 _6 l
  42.     flock($fp, LOCK_UN);
    + e0 j5 i3 ?1 l2 A! u" L5 R" B; y
  43.     fclose($fp);
    / E0 X" B3 V9 b8 k1 B* F

  44. : t7 H. S7 h7 r% T. Y1 T8 G
  45.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    ' m# ?" e; e3 h# V
  46.     // while($row = mysqli_fetch_assoc($res))0 e( t" ^2 M5 q7 |# s3 i" k
  47.     // {$ X, r. t" e" [/ s  F7 h" F
  48.     //     $id = $row['id'];
    0 R$ |# Z: g( w8 _$ p: Y) D  ?
  49.     // }/ R2 c; l2 D  ]1 [6 T8 y6 }
  50.     // echo $id;
复制代码

# s) `5 t% i" G* }. F" e' L0 _! |$ ^" M' N/ Q2 ~! ]
抢券活动实例:
  1. public function envelopeSnatching(){/ O/ ?( m- H5 U3 C
  2.         $lingqu = $_POST['type'];
    $ I1 P4 X. N* M1 l7 u" f" f0 ?7 D
  3.         $uid=session('u_id');//用户id+ {( u1 z* I  x4 R# H' E5 T1 b
  4.         if(!$uid){
    9 x" L, B  y+ s
  5.             $data['msg']='您没登录,请先登录!';
    % g) F) \: ^$ S* w
  6.         }else if(date('Y-m-d') != '2017-12-12'){
    * l$ F5 t# |5 D$ h, _, k' x  c
  7.             $data['msg']='不在活动时间内!';
    $ k2 D. }0 ]4 J! H& e0 m. h7 E
  8.         }else{
    ! b+ E2 C: Q6 H( \9 X6 Y. B4 z( l- v
  9.             $hours=date('H');//当前小时数1 Y1 U$ o% r$ s: O, I; s
  10.             if($hours > '09' || $hours > '17'){. x* s0 F8 `( d

  11. * z) C) |4 O5 l6 M4 |9 M8 w
  12.                 if($lingqu == 1 || $lingqu ==2){//点击10点的
    . U3 }4 ?" j  F* j( f
  13.                     if($lingqu == 1){
    ! }0 u. ^7 Z* \0 u
  14.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过9 z. Q; @% m: t  E
  15.                         if($hours > '09'){4 j9 Y2 q" Q! m; m* L# ]$ z
  16.                             $num=mt_rand(25,28);//优惠券金额
    . J1 V* _" J( @0 C, D
  17.                             $id=1;
    1 \7 F0 d0 m+ V" p& e) l2 c1 s
  18.                         }/ t7 c/ r; l. {1 O& _3 T
  19.                     }else if($lingqu == 2){8 |0 J7 k" Q$ J0 s) @. v7 K/ [) S* w
  20.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
    " J' O2 q# ~/ Q) s' i3 w
  21.                         if($hours > '17'){, F+ z+ y; ?  X- s
  22.                             $num=mt_rand(50,55);//优惠券金额+ y3 [2 s* l4 z! F/ i5 _
  23.                             $id=2;/ u, E. y9 U0 y% z# w
  24.                         }% w; k; c( A, l, y, `! q8 \
  25.                     }
    / G# d" R. F7 L" K9 [. s
  26.                     if(!$id){
    4 o; L8 p, J( D- Q" J4 x8 k$ C
  27.                         $data['msg']='时间还没到,晚点再来吧。';
    2 f* H( J. s* g' V  T1 f" \% G
  28.                     }else{
    9 V# J* C, d8 ^3 s* S
  29.                         if($is_lingqu){
    2 u' m  N5 }8 Z
  30.                             $data['msg']='你已经领取过了,留点给别人吧!';1 o1 C" G# P3 s$ _
  31.                         }else{
    5 F5 X0 H0 R' S
  32.                             //锁表
    1 D  g" X1 F+ K1 f. M% Y
  33.                             M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');1 s% F: D  O. b( x1 X' m, p
  34.                             $active=M('active_num')->where(array('id'=>$id))->find();
    2 j% v0 `/ q# `4 v) _3 b$ B
  35.                             if($active > 0){
    % i$ t7 h( [/ w! a3 Q& j4 K# o
  36.                                 //开启事务
    . `) E: F% A- s( ~3 U8 ^$ l
  37.                                 M()->execute('start transaction');7 E. n6 ^: q* d3 M. q- {
  38.                                 $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));# P* o- Y% |  a6 A
  39.                                 $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
    ( y# s  ]2 G9 x  c4 z* u
  40.                                 $members_preferential    =    M('members_preferential');
    ) o+ k1 V, r+ p: W- A0 f/ d
  41.                                 //对应投资金额,+ x8 b+ s3 [4 d7 B
  42.                                 $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));5 z4 ~% I2 _1 u) w* Z6 L6 W; C: G
  43.                                 $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');) X$ F  v6 y3 r. ~& a# f
  44.                                 $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
    8 r# m* u; F6 r- l4 r+ ]
  45.                                 $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券; p( u* n- l9 A0 e" I& L+ `
  46.                                 if($save && $add && $add2){. S; p0 H1 x( m# g- w$ S/ A
  47.                                     //事务提交
    ) H8 B0 w' z4 W5 L! ~9 T+ V% K$ Q+ y- R
  48.                                     M()->execute('commit');$ ?. |3 B; G0 c, T. `# x
  49.                                     $data['msg']='恭喜你领取了'.$num.'元优惠券';
    $ O2 ]) b5 D7 N) k$ y
  50.                                 }else{
    : x, A! K2 s- j3 K' v" {8 d
  51.                                     //回滚9 m+ C2 p: C! r* y  b# s
  52.                                     M()->execute('rollback');6 r- V+ a3 ^! W0 K3 ?
  53.                                     $data['msg']='未知错误!';$ N' B) O/ Y4 {
  54.                                 }
    & P" s  i8 ?& e: J5 W8 ]6 o
  55.                             }else{+ G; H: @* L: ?( B) S
  56.                                 $data['msg']='红包已领完,你来晚了!';
    9 J! @* l: p5 _
  57.                             }
    ! I  U4 A. ^. ~
  58.                             M()->execute('UNLOCK TABLES');0 }" P; g% z* K9 C5 ^% ^! A
  59.                         }
    * R8 k/ C8 p: `; F7 G1 Y7 n8 \- b
  60.                     }2 b4 E6 g% @2 V7 h6 B/ y4 m( Q$ x
  61.                 }else{  m& m. N% G0 L2 Z7 S# ^8 s
  62.                     $data['msg']='非法操作!';
    ( j; J8 f, c4 a5 Y$ i/ p4 Y
  63.                 }
    ! h$ B; P4 _6 j0 s( p$ w
  64.             }else{
    + m6 C0 E7 u1 e' n/ m
  65.                 $data['msg']='还没有到活动时间,请晚点再来哟!!';' M) B9 G: `; m, [
  66.             }. ?0 W4 `. Y" D7 S. c( j
  67.         }
    7 J5 s1 z* ]% B! k
  68.         exit(json_encode($data));& n" L' l1 R4 z
  69.     }
复制代码

6 i- y* v* S3 ?+ j0 E$ t
& ?" e% P4 v8 G2 ]0 x




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