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

8 [3 j* P4 J' i& y: K( |2 \2 O. H
Mysql中的锁语法:. w8 U) J& Z& t" o8 c$ P, Z; U- \
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
$ j% D' f1 A9 Z' m8 |9 HUNLOCK TABLES  【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
6 z: c, ]' j# m0 T$ Z: q; l% vWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
: R: j5 P0 x- |( H& C! b7 ?注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :' |! N' z7 }% K; L+ [
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。  N4 t; Z7 M! ^
测试时,有个文件就行,叫什么名无所谓
总结:
0 Z' T* I7 b$ J. O8 `# j项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:/ C' k# H# M) }" U7 n! [
1. 高并发下单时,减库存量时要加锁
0 i- }* z: M! m5 E6 J; j- u$ ~2. 高并发抢单、抢票时要使用
Mysql锁示例:
  1.    /*% @' E) n9 X2 ^" o$ h! A; X* ?
  2.     模拟秒杀活动-- 商品100件7 n4 U. W! }% K: k8 l9 T9 P4 C
  3.     CREATE TABLE ta% }: u1 n4 F" s  d3 J
  4.     (! H' z; d& N6 @* s* ?: h3 y! F& T
  5.         id int comment '模拟100件活动商品的数量'
    4 l9 p& a2 q# x2 i( z6 j% k9 Y! _
  6.     );
    ' ?, t& d6 j. z$ I. a
  7.     INSERT INTO ta VALUES(100);
    8 G+ s* J7 ?% Y% U1 N% d
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件
    ) v7 ^8 L! K+ O: i1 P1 }
  9.      */
    4 i; N: `6 C" x- ^' Q5 N3 ]
  10.     ) c0 B2 O0 H1 d& P* N, ~. i
  11.     // 关闭错误报告
    5 U5 k, _6 l5 \% H
  12.      error_reporting(0); $ X) f  z0 t- U, x: i

  13. % B& X; m$ k, v* u3 L
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址
    * e( f9 s; t* S
  15.     $dbuser = 'root';            // mysql用户名) P2 |1 v9 L0 k3 f. P4 ~
  16.     $dbpass = 'root';          // mysql用户名密码
    ) n% s+ H: Y" d: _* D1 E
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
    ; a. b1 v8 g$ w. n+ P3 w7 K  ~1 N
  18.     if(! $conn )) Z2 C# z4 e, _& Q1 @
  19.     {- ~9 b1 x, S9 F* g% o
  20.         die('连接失败: ' . mysqli_error($conn));) ?. m' N% y$ }& B( o
  21.     }
    / [4 p' w& F# f( O( i, ]" P1 u
  22.     // 设置编码,防止中文乱码2 ~8 p1 t# M+ p" p. \# |
  23.     mysqli_query($conn , "set names utf8");  a# c, X& I3 {4 ]6 {
  24.     mysqli_select_db( $conn, 'temp' );
    7 Q8 q( ]) ]+ I  S" P, T, B. C
  25. ) \( y* p( x6 {
  26.     # mysql 锁
    & B8 E5 J# O. f$ i
  27.     mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
    ( s7 {5 s( V' E7 m3 }* i$ ?
  28.     $rs = mysqli_query($conn , 'SELECT id FROM a'); ; s- A- f) F+ l1 s+ M2 @4 ?+ B
  29.     $id = mysqli_result($rs, 0, 0); / O: ?( l$ _7 v, A" ?3 p9 i
  30.     if($id > 0) # P! I( P& q6 V$ }8 J! R5 a" ?
  31.     {
    " v  b, S" v% }. S) Q
  32.         --$id; " \% D! [2 _6 ]6 V4 J+ I! j8 U+ B
  33.         mysqli_query($conn , 'UPDATE a SET id='.$id); " V9 N/ P6 `% i) N
  34.     }
    2 Y2 c. M& O6 u. k6 b; P
  35. - A: M* Z& g: A$ |" I
  36.     # mysql 解锁
    + E7 y& `' |1 }
  37.     mysqli_query($conn , 'UNLOCK TABLES');' l3 n5 R3 B7 w7 A0 l& ?  [
  38.     //查询解锁后的id值
    * q2 {$ A& Z& S3 T  P
  39.     // $res = mysqli_query($conn , 'SELECT id FROM ta');6 W1 x- p4 i8 i1 |. g) x' j
  40.     // while($row = mysqli_fetch_assoc($res))
    , w# o$ Y" G( m5 B5 Y7 ~
  41.     // {
    + }2 V- \- \$ @( C+ y
  42.     //     $id = $row['id'];
    6 f$ C) [4 _! w% H; a; o2 u4 }
  43.     // }5 f: Q4 q" z$ W, @3 a7 i8 r
  44.     // echo $id;
复制代码

5 N/ ^9 u& [% H  W0 Z1 Z5 K/ N; {/ r1 u) f, \* H

0 W- d! C# H! c! T
PHP文件锁示例:
  1. /*
    $ x8 F+ E- c. ^" [* g) ~; U
  2.     模拟秒杀活动-- 商品100件
    0 X( p$ w( Q: M3 ~/ X3 M5 F- i
  3.     CREATE TABLE ta
    0 X+ I3 H5 v& x) ?4 i- l3 H
  4.     (7 ]$ Q* `: k- K) F9 r5 J# c5 o2 t
  5.         id int comment '模拟100件活动商品的数量'  W$ f0 V" @- w0 s# P: F8 q
  6.     );
    . b5 \2 r( k6 x! J) l! H
  7.     INSERT INTO ta VALUES(100);8 J! S- k0 b, E9 K$ H
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件( u  i" u6 a# O) G( K0 m
  9.      */
    % }% O" Z( [- a) h6 s7 K; a
  10.     / e& l+ u* L& [3 ]" n% d% i5 ?
  11.     // 关闭错误报告- h# j3 p0 y  b3 c" n9 N& k/ g7 \
  12.      error_reporting(0); : e2 R: i( s6 l4 V( P; I

  13. " y7 S: v1 h8 J* N: \' e
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址
    9 d: X5 d- ~/ W; x2 m
  15.     $dbuser = 'root';            // mysql用户名1 m+ }* r& J$ B
  16.     $dbpass = 'root';          // mysql用户名密码
    2 ~. Q3 ]$ h! C9 R
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);( K9 s$ m4 X! {7 l. \4 v/ w# P
  18.     if(! $conn )
    $ ^$ K3 _2 }+ a8 ?4 Z7 l
  19.     {
    7 a  Z% j8 f+ U8 I
  20.         die('连接失败: ' . mysqli_error($conn));
    % ?3 l0 w' J+ \+ t& ^( d% c: y. E
  21.     }3 \' V& G0 k  G% Z' ~5 ?$ I+ c8 @
  22.     // 设置编码,防止中文乱码( W6 X" B9 b7 F/ |! a
  23.     mysqli_query($conn , "set names utf8");
    - d5 O7 Q( x+ s# x
  24.     mysqli_select_db( $conn, 'temp' ); 9 }6 n6 O9 Z' `1 I4 p( }: Q, Z

  25. 7 ?+ Y* c1 @( r
  26.     # php中的文件锁 / |- C/ M: w" d. v* N
  27.     $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 8 O% {: Q: k' V* c
  28.     flock($fp, LOCK_EX);// 排他锁
    " @, T, ^3 `* A

  29. 1 H, N7 o  h8 j4 F+ W, r! y* d4 K" a
  30.     $retval = mysqli_query($conn ,'SELECT id FROM ta'); 1 _3 h5 c2 Y3 Q  Z
  31.     while($row = mysqli_fetch_assoc($retval))9 r  }8 J# G0 S
  32.     {/ m+ a7 j, X. s. X
  33.         $id =  $row['id'];) B( ]8 x+ |6 B
  34.     }, u0 {0 U7 ]7 |: S
  35.    
    . x: H5 ?1 T% D! L! p5 E
  36.     if($id > 0) ' P" q* u/ b8 t  A0 P! C4 O  t: u
  37.     {
    8 L5 |- j+ {8 d4 [
  38.         --$id; 9 p4 \1 ?" J1 v1 \3 r
  39.         mysqli_query($conn ,'UPDATE ta SET id='.$id);
    : B% k! u$ B  X# G* Q& x% Z
  40.     } 6 r+ P% F8 }% r+ S( }3 i% J; r6 N0 U4 G
  41.     # php的文件锁,释放锁 ' M+ c* {5 Q) q/ N
  42.     flock($fp, LOCK_UN);
    . E6 V* s2 b; v
  43.     fclose($fp);
    , L* [, ?7 N% a# O

  44. ; w3 k5 P" _! N* U! X
  45.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    ' T$ m" l7 g6 I# C' ]
  46.     // while($row = mysqli_fetch_assoc($res))  g& X4 a) w* U" }/ r: A
  47.     // {
    ! [; {- h! j) ?8 q; G; L
  48.     //     $id = $row['id'];
    ' \* n# W6 G1 x: w# M( V
  49.     // }2 t2 h  U, a( [# L* E
  50.     // echo $id;
复制代码
5 {$ P2 k0 l/ _) u* M: c$ O
) q1 b, C6 _6 c; d. N; Y4 R, @9 B
抢券活动实例:
  1. public function envelopeSnatching(){
    " j- @# T) B6 G$ A# ^2 `
  2.         $lingqu = $_POST['type'];
    % o6 U0 {4 e& }3 k9 o
  3.         $uid=session('u_id');//用户id1 O2 |' @3 v' @/ s
  4.         if(!$uid){
    ' c- [: X2 f! k9 G: s! e$ Y- F7 o( Z
  5.             $data['msg']='您没登录,请先登录!';
    ; F- I% d/ M+ K# J, T) j
  6.         }else if(date('Y-m-d') != '2017-12-12'){
    6 W5 z3 t& Y6 v2 _% x7 A6 K
  7.             $data['msg']='不在活动时间内!';6 l+ t6 _6 x+ D: i" N7 g, F
  8.         }else{% I. h9 Q) p( C: e+ R
  9.             $hours=date('H');//当前小时数' k% @1 J+ L1 c4 ]) C# y! m
  10.             if($hours > '09' || $hours > '17'){, R% g) E; R8 L
  11. ) G5 v9 U% D7 b9 o# d/ g& m, Y
  12.                 if($lingqu == 1 || $lingqu ==2){//点击10点的; Q, ?/ m" N- ^: z
  13.                     if($lingqu == 1){
    & F* ~# T+ o" B$ z1 I
  14.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
    4 H  I- z1 v7 M
  15.                         if($hours > '09'){
    - J4 i/ E; z0 m2 H5 K" U2 O7 K8 B
  16.                             $num=mt_rand(25,28);//优惠券金额3 h' p6 Z, D% D5 a- j
  17.                             $id=1;( M' l5 c  t2 p
  18.                         }: |- r0 W# s! N  l6 r' x; k! M
  19.                     }else if($lingqu == 2){
    . c6 T% m" d, A- d8 P; }( O
  20.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
    4 A; [: t' A$ l; v( N9 I
  21.                         if($hours > '17'){8 u' c* ]+ D  \" u9 w- W
  22.                             $num=mt_rand(50,55);//优惠券金额
    0 p) H0 K* U7 J" Y8 C/ t
  23.                             $id=2;, J& H' P1 @5 N2 _/ F
  24.                         }6 p) L% }4 ^7 g8 d9 D, w
  25.                     }. n! m3 g6 l! W, o
  26.                     if(!$id){$ \7 C% k9 N- |: {( P* f8 W
  27.                         $data['msg']='时间还没到,晚点再来吧。';/ T5 m* |1 u0 t0 \& l
  28.                     }else{
    7 h! C! J. F5 r/ u# H8 x
  29.                         if($is_lingqu){8 b7 C' P4 I) Q) h( w
  30.                             $data['msg']='你已经领取过了,留点给别人吧!';) {9 |* E6 Q0 }5 i# `# Y/ u
  31.                         }else{  G7 M) q4 Q. w) u$ F
  32.                             //锁表
    # h* N% Y' o0 {
  33.                             M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
    : [, [+ o- A9 r+ _& |4 g2 N' X
  34.                             $active=M('active_num')->where(array('id'=>$id))->find();3 f7 `; c1 t3 ~. r1 i! }5 l. Z. Z: d
  35.                             if($active > 0){' m' _; G( d- r* [( C. u
  36.                                 //开启事务" W3 O# v# P% M& W6 m/ L* g+ ?
  37.                                 M()->execute('start transaction');
    ) d0 K' C! _9 S9 X! y
  38.                                 $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
    & {7 W) v! A% I7 E; e. w' S
  39.                                 $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
    6 I4 x$ L5 e+ a1 G
  40.                                 $members_preferential    =    M('members_preferential');
    ( y$ t7 `( g' M* v# {# l
  41.                                 //对应投资金额,
    1 c; }# g) _) w/ }( G' b+ O8 U
  42.                                 $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));! W3 U4 p. U0 f% l2 M
  43.                                 $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');9 a; I6 O& g+ A; a
  44.                                 $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
    & ^/ P, T3 c. ~& Q
  45.                                 $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
    : Q& ~% i  G5 n1 ]& P
  46.                                 if($save && $add && $add2){5 L) p4 w* P0 ~5 {
  47.                                     //事务提交  K& j' |- V% m  w: v# ~5 j6 c' P
  48.                                     M()->execute('commit');8 W- [- Y) n' }3 c% }7 }& H( v$ g
  49.                                     $data['msg']='恭喜你领取了'.$num.'元优惠券';
    4 j* U' z0 D) w( E) m
  50.                                 }else{
    $ |# U& u7 z. p8 C; J/ h
  51.                                     //回滚
    3 }( E' d# v! j% n! M/ y1 ?
  52.                                     M()->execute('rollback');
    1 ~: _& x' R; x1 ?% [" n
  53.                                     $data['msg']='未知错误!';( l9 s! r7 u. z$ C! {) V% P
  54.                                 }( J6 W% Y& ?) F
  55.                             }else{
    " o% M; r5 ^* ^) ?0 Y. r; D/ y
  56.                                 $data['msg']='红包已领完,你来晚了!';
    ! P; ^1 u! H* `& ?* j- t
  57.                             }
    " C9 D4 u: S5 i8 z, B; k* P4 X
  58.                             M()->execute('UNLOCK TABLES');
    - B7 r' S# n+ x9 _: o
  59.                         }4 {( ?* ]% r8 H  o% n6 [3 A0 `
  60.                     }; V# A0 W) ]! g  V
  61.                 }else{. M" s$ z0 u" m9 ^( g+ N1 D
  62.                     $data['msg']='非法操作!';4 `" K9 ?$ z6 E; [0 D
  63.                 }$ c8 y" h9 p$ s! K* i/ \7 i
  64.             }else{
    & n0 m+ w3 M5 F! T1 h
  65.                 $data['msg']='还没有到活动时间,请晚点再来哟!!';& s3 C( K7 g1 I7 i3 ^0 I6 F) f
  66.             }
    . n+ c  ]" @7 }* w+ P
  67.         }
    6 V6 O- {# w& l( t8 Y! J
  68.         exit(json_encode($data));4 o' p, T# x- d9 N  V( X, Y- U
  69.     }
复制代码
8 N* X  f1 B! G
! O$ {& T6 t0 |/ \6 X5 H' F





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