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 x4 i# C: }# O/ W  R$ F
Mysql中的锁语法:$ z' E" a4 Q  ~$ r, a: |6 T/ k
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】4 X- u" M, C4 D  V* D0 k
UNLOCK TABLES  【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表; ]# g4 Y+ o3 s3 {2 \5 M: s
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞& \, V3 I: ~" I% w9 c* Y
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :
7 _) w: n. L; S7 p7 g- j) u7 O9 y6 k* \$ G% s文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
4 x5 \- \" _. \  O测试时,有个文件就行,叫什么名无所谓
总结:
$ K/ \( S' u! X& y项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:
& L6 M, w  V4 j$ S$ X/ n1. 高并发下单时,减库存量时要加锁
- p( j+ [+ A" o6 a- [6 C2. 高并发抢单、抢票时要使用
Mysql锁示例:
  1.    /*
    # `; d; d9 p" q/ h: x. @  u" x
  2.     模拟秒杀活动-- 商品100件* @, Q. y- C$ K) ]0 n5 d
  3.     CREATE TABLE ta0 e* F2 x% O( g8 D* y5 a
  4.     (4 t& I3 b( J1 M9 _$ ^) \
  5.         id int comment '模拟100件活动商品的数量'! H, U+ K$ T- {' t; F7 e
  6.     );4 |, H! V8 r6 r3 [( v! k5 {
  7.     INSERT INTO ta VALUES(100);" f% r6 O- a; w0 I" |6 O( i
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件$ J& a. Q6 V- r8 }
  9.      */
    7 l# H4 U( e. t5 w# W. U& o
  10.     % {$ L$ o7 y2 ?( d$ b1 k
  11.     // 关闭错误报告( D5 {* O4 G0 Z6 n# i7 Z
  12.      error_reporting(0); 4 k4 k- `* J. T2 F
  13. : ]2 \# _  s5 M' z
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址- W* [! H! g7 W' u
  15.     $dbuser = 'root';            // mysql用户名
    ) P, S; I+ H- f. i/ c
  16.     $dbpass = 'root';          // mysql用户名密码  q8 }  Q% @9 v
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);: e* G) y8 Q3 S5 A! Y% n9 y' I
  18.     if(! $conn )
    , O7 ^. P$ \0 u# r
  19.     {$ `% p- z' z& d- z3 d
  20.         die('连接失败: ' . mysqli_error($conn));
    0 C" p* `  M! a9 k9 |" t2 `# Z, \0 s
  21.     }
    9 e: n8 r) |6 x
  22.     // 设置编码,防止中文乱码
    5 l' h$ s# Z3 C# `  `* d
  23.     mysqli_query($conn , "set names utf8");" N, s  e6 V. g- h
  24.     mysqli_select_db( $conn, 'temp' );
    & R% P# o8 c- o& ^9 k$ Z+ |

  25. % R* w# ~' q; L9 X- q
  26.     # mysql 锁
    " b2 P  s+ i0 X5 T: h3 c) e' d7 p* R
  27.     mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
      u7 w6 c7 T1 I, \
  28.     $rs = mysqli_query($conn , 'SELECT id FROM a'); * J% I0 c1 p7 f0 O: m# \
  29.     $id = mysqli_result($rs, 0, 0); 1 X+ }$ L' W5 i
  30.     if($id > 0) ( Z1 d5 G) f$ d* m
  31.     {
    $ I. u& W6 b2 z+ W
  32.         --$id; % u  C& m# ^- t
  33.         mysqli_query($conn , 'UPDATE a SET id='.$id);
    3 V; X4 y5 M* e& [
  34.     } 6 s6 C7 D$ |0 `, c/ c$ V5 ^- u
  35. 2 o3 x  b/ v1 u% \; ^, o
  36.     # mysql 解锁 ( H) u" H7 Y; {0 {5 o9 `. I
  37.     mysqli_query($conn , 'UNLOCK TABLES');6 g% c1 `$ m2 I" R3 e, W! X
  38.     //查询解锁后的id值# f) L, ]6 S  y- j
  39.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    ' s9 [( E. p1 j$ q2 t) t
  40.     // while($row = mysqli_fetch_assoc($res))
    ! R  @" N' h5 I( b: b/ y
  41.     // {
    2 y0 W) W$ s9 P1 b5 u' N8 m
  42.     //     $id = $row['id'];7 Q6 }( J: y: J+ Z- ^2 y% ^
  43.     // }. K! E3 J' H, _
  44.     // echo $id;
复制代码
( i0 p9 q, V$ w0 L
5 p& w4 \/ t: Z; |! m$ Q) Z' v
+ t* O1 |) |$ D9 M$ R: f
PHP文件锁示例:
  1. /*4 e  b$ T( w3 t! {0 U+ k, x
  2.     模拟秒杀活动-- 商品100件
    5 }  A9 N3 i, I$ y5 h# N
  3.     CREATE TABLE ta
    / |9 r( \; |- c1 ^4 C7 x+ C
  4.     (% t$ Q/ t, v1 ~! B' {
  5.         id int comment '模拟100件活动商品的数量'" b9 {" G1 u7 z* x
  6.     );" A" i  t' X( C( v4 t7 F7 m
  7.     INSERT INTO ta VALUES(100);% p& Y. U: o- C2 `6 E8 Q4 b
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件3 ?0 x) U6 P$ O, G* D5 F
  9.      */
    7 b4 @$ m4 \! b* r' K. I7 n
  10.     ; ?9 t0 D4 C8 `5 b* R$ R/ l7 y* k
  11.     // 关闭错误报告
    % c, O* B6 q' \) s7 K
  12.      error_reporting(0); , ^5 m- K; [) E0 w

  13. % g$ P; `: {7 L: c
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址% q+ A2 j$ v/ R* O6 v0 H
  15.     $dbuser = 'root';            // mysql用户名
    , s2 L; U3 _, L/ L
  16.     $dbpass = 'root';          // mysql用户名密码
    1 x! J6 n& P" P) A4 Z4 C# U
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
    8 n  w3 |4 G& @
  18.     if(! $conn )9 V% n/ K7 }+ r% a
  19.     {% v% `, p8 Y+ N
  20.         die('连接失败: ' . mysqli_error($conn));; o. p9 p% o  ~3 j1 o  \
  21.     }$ {- I) t% q  I
  22.     // 设置编码,防止中文乱码
    ; C; S1 ]3 ^4 u3 j" \  \
  23.     mysqli_query($conn , "set names utf8");* e2 F% Y" z1 d9 w) R
  24.     mysqli_select_db( $conn, 'temp' );
    4 q2 M% D7 p* i. ]
  25. 2 A, w2 H+ E) Q5 p$ ^: K
  26.     # php中的文件锁
    ' a: B7 `8 c1 X& k8 a7 W
  27.     $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 - V) \& H1 E: i3 s1 H" U& Y
  28.     flock($fp, LOCK_EX);// 排他锁 * T9 Y: N* }; X( \& G" F2 h
  29. ) W. s' `6 I  r( c; S8 R
  30.     $retval = mysqli_query($conn ,'SELECT id FROM ta');
    , _- K# \0 ?1 M! s" [1 M
  31.     while($row = mysqli_fetch_assoc($retval))1 n8 ^+ {) `2 h
  32.     {
    - G' G. j; e! a# \# W& V  W
  33.         $id =  $row['id'];
    2 i, v! D: l3 \  |' ?$ {! r
  34.     }5 `0 ?( _; J+ p# d6 a0 s" o+ [0 C, V
  35.    
    ( G# N! Y  f' \1 H7 r
  36.     if($id > 0)
    ; j4 a* `- C" k" }2 L0 r
  37.     { & Y' Q, s! P' m8 S  V% G* }
  38.         --$id; $ a0 C, a) f# p  f1 Y
  39.         mysqli_query($conn ,'UPDATE ta SET id='.$id);
    - W# Q& C2 e/ C) d0 A+ |
  40.     }
    3 f3 a$ h0 X$ W& y  N
  41.     # php的文件锁,释放锁
    * V( O& ^5 e( ~" Y
  42.     flock($fp, LOCK_UN);
    / T* z7 M, }4 A' k
  43.     fclose($fp);+ D3 [: ]/ r1 `$ L3 [- J$ q! n; p$ @/ h

  44. * z1 v3 x+ x6 w5 P9 a  E# x
  45.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    1 g% Q: L" A8 S' x* N; L) g
  46.     // while($row = mysqli_fetch_assoc($res))
    ; ~2 l7 J& Z( P% M0 M" O
  47.     // {
    % i& U  O- n% c. q0 F* `
  48.     //     $id = $row['id'];
    + L6 k2 M+ K) Z, w4 i: y
  49.     // }
    4 d+ J, ?% H# ^% g3 \. ?
  50.     // echo $id;
复制代码

7 W' W" y$ Q3 M( ]
9 K0 U1 b" `8 o
抢券活动实例:
  1. public function envelopeSnatching(){) |0 ?: |+ s" N3 j! x" f
  2.         $lingqu = $_POST['type'];
    + Y3 v) m; A& q  v5 X+ L
  3.         $uid=session('u_id');//用户id
    $ y5 X# y& _+ B0 s
  4.         if(!$uid){
    % @$ _; K6 c. S  v
  5.             $data['msg']='您没登录,请先登录!';. Y1 m1 I; i6 p: B" E, X
  6.         }else if(date('Y-m-d') != '2017-12-12'){
    - g4 g8 z! \- [
  7.             $data['msg']='不在活动时间内!';
    + V! N" G8 ^2 @4 n2 X, T
  8.         }else{' A# d3 @$ B  F) o$ P; e3 H
  9.             $hours=date('H');//当前小时数6 P9 H1 \6 G- N5 U/ w% N& ?2 W: t3 L
  10.             if($hours > '09' || $hours > '17'){6 m9 ]9 x5 y7 V7 k9 c8 a3 q

  11. # I& M5 s! y3 r" L$ r: Q8 ?- ]
  12.                 if($lingqu == 1 || $lingqu ==2){//点击10点的: U& K# x5 Z9 C% i" E
  13.                     if($lingqu == 1){
    0 O; b- |# Z+ E* u  x: I- m! P
  14.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过0 D/ J" L! l2 f/ s/ j6 {' L
  15.                         if($hours > '09'){' X8 |6 h8 m7 h! \
  16.                             $num=mt_rand(25,28);//优惠券金额, [$ g" k) t+ l1 |) Y1 ]
  17.                             $id=1;! g, N' e1 x5 }" {( k' \2 L. ~
  18.                         }- l: \' \- N0 B3 ]
  19.                     }else if($lingqu == 2){
    " X8 }, z. V& L1 j4 c7 R0 V
  20.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
    3 q* L* C& B$ q9 E
  21.                         if($hours > '17'){
    , E% p9 O4 |) @/ P9 L- g4 q4 o! |8 ]9 M$ P7 R
  22.                             $num=mt_rand(50,55);//优惠券金额
    3 V: u8 Y! N; ^6 M9 L0 Q! m
  23.                             $id=2;- _% I9 C" f' W# M- H
  24.                         }* d& B8 z1 L( r- {" f
  25.                     }
    1 T4 ^! |1 V: g, y* y5 K& V6 K' R
  26.                     if(!$id){
    - `" ?  e# r: m4 x4 `5 U( [
  27.                         $data['msg']='时间还没到,晚点再来吧。';: n6 c7 U6 a' `2 `1 V& U# g( B4 \* ?
  28.                     }else{7 \8 @% K" O. k  P
  29.                         if($is_lingqu){
    - {/ E  Y6 w' h" ^4 p/ w  F
  30.                             $data['msg']='你已经领取过了,留点给别人吧!';
    4 x8 w2 {4 I; E* B7 Y: D  R8 z. ?
  31.                         }else{
    3 t% \9 h( f, M2 l9 T% q' p
  32.                             //锁表" s9 D8 g, B! }7 l- I  N+ Q1 p0 ~
  33.                             M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');0 W: D3 v' P$ B: m7 C& ?* }+ T
  34.                             $active=M('active_num')->where(array('id'=>$id))->find();
    ' M6 w2 l# q; {* a
  35.                             if($active > 0){
    ) G$ ~" a' g- @
  36.                                 //开启事务: e7 v, s9 Z( |" d$ Q4 Q' ]5 C
  37.                                 M()->execute('start transaction');( s* {2 E' F3 f! }1 S  p! D- }3 K% E
  38.                                 $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
    ) I2 W( ?+ u# B* Z9 I9 T$ }
  39.                                 $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));3 a6 x7 O8 P% @
  40.                                 $members_preferential    =    M('members_preferential');
    8 U$ \7 S2 A& p0 r8 w
  41.                                 //对应投资金额,  D* g3 W: K- a4 z* V- I! W4 @
  42.                                 $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
    / j5 Q$ `- q9 E/ `7 W+ r$ h
  43.                                 $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');# _4 T5 V' [! z, q; D8 H. L
  44.                                 $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间: d% b8 `5 f$ O4 f3 o
  45.                                 $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
    & _4 C" G4 ^" e5 I0 W
  46.                                 if($save && $add && $add2){  g2 {6 ], Y- d, D/ o; R: D3 G
  47.                                     //事务提交
    $ n6 a9 r5 I; A
  48.                                     M()->execute('commit');
    : A& B' h6 {+ n' D$ [( t& D- I0 G
  49.                                     $data['msg']='恭喜你领取了'.$num.'元优惠券';$ U0 C3 ^2 ^+ d3 U5 N+ `* e
  50.                                 }else{
    $ v3 f, A5 W: n. D
  51.                                     //回滚* N: W' f8 C& P# z
  52.                                     M()->execute('rollback');; u- q1 ~7 g7 K' t$ W' P& e* W  m
  53.                                     $data['msg']='未知错误!';
    ) R' ^9 C. f* u3 c5 h* U' c
  54.                                 }* w" w( @3 P1 K1 k; r4 z
  55.                             }else{
    & i& S: o# x9 }+ E% d
  56.                                 $data['msg']='红包已领完,你来晚了!';+ h$ O! _; g- j
  57.                             }8 ]! P1 I% W8 n+ _0 M( v# K
  58.                             M()->execute('UNLOCK TABLES');, j' c8 V  D/ j6 T4 G/ ^
  59.                         }5 z- P0 q1 u0 G$ D
  60.                     }- M4 ~: W2 ~$ w6 k( |! Z5 R
  61.                 }else{1 s4 H2 j) `. P% m
  62.                     $data['msg']='非法操作!';
    / V8 f6 h* W% E9 B
  63.                 }; K5 @9 J9 L" d0 N
  64.             }else{
    # H. \& i8 F1 t% |; d3 T. l
  65.                 $data['msg']='还没有到活动时间,请晚点再来哟!!';
    2 X+ j- K0 ]$ s7 E9 C
  66.             }, M( K6 ?& J, N
  67.         }
    ; n( j& c! ^  m' D  D; _4 |6 S
  68.         exit(json_encode($data));0 g8 y/ k! y! ]9 o
  69.     }
复制代码

* h. K4 p% d6 k$ R( ~" O$ }/ U: u2 N0 _2 P# H0 r- E$ H9 q1 }





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