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 B  A0 B  L; a
Mysql中的锁语法:+ _. O9 p! h. R* o
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】) `7 P' ]7 F$ U4 d, \
UNLOCK TABLES  【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
4 O& f6 G% W! w& `Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞- h: u6 n9 s3 q, w4 O
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :! O4 J6 m$ p6 a
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。6 n4 y, W2 s- I
测试时,有个文件就行,叫什么名无所谓
总结:1 v- l* D+ x5 ^
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:
( _7 |) w+ ~0 W: s. v9 o1. 高并发下单时,减库存量时要加锁
: B2 z: @7 f; Y2. 高并发抢单、抢票时要使用
Mysql锁示例:
  1.    /*" x9 Z( t- j& X7 ?
  2.     模拟秒杀活动-- 商品100件
    * l3 y% |. \5 z5 u* N
  3.     CREATE TABLE ta
    6 k9 s, r- t7 y* S
  4.     (
    * q5 V4 T; R; _( g0 P* L( e
  5.         id int comment '模拟100件活动商品的数量'; z- w( a7 v5 `+ ~& \% L5 u! D
  6.     );
    8 h% U4 j: p: i* H( p
  7.     INSERT INTO ta VALUES(100);
    6 w, R" b; n7 }; T
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件+ Q/ b' X! @' q! H, h. R2 W/ f
  9.      */
    # n$ S) ^0 y8 O! f8 Y+ ~, V4 u0 h
  10.     $ i. F+ {& {0 e( w4 A
  11.     // 关闭错误报告& }3 t% w+ E8 t! V7 \4 {
  12.      error_reporting(0);
    : |8 b) f$ R( J7 |9 B, M

  13. ( x. K! ]7 R. }4 i( v5 j! _. X
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址( |+ d1 z* T3 A. ^
  15.     $dbuser = 'root';            // mysql用户名& Z& \1 b  @9 ^' }+ }
  16.     $dbpass = 'root';          // mysql用户名密码
    # p& T: |: m6 `2 d5 J
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);/ b6 N' v, W6 X/ M8 }; ^3 N; X8 }1 k
  18.     if(! $conn )4 r6 O4 X/ z3 J4 v/ S/ j8 z
  19.     {- _. M* R2 o- s# v4 n0 Z( H
  20.         die('连接失败: ' . mysqli_error($conn));  f: o- ?  H8 G1 r
  21.     }5 s' T4 C- k/ R0 }9 l+ k
  22.     // 设置编码,防止中文乱码
    + i) K! M4 p: K* N# _, q3 D* D
  23.     mysqli_query($conn , "set names utf8");
    ; P( s9 \: `  Q1 c, C
  24.     mysqli_select_db( $conn, 'temp' ); ; D2 l5 ?8 B6 L( f' l8 D
  25. ) i- p6 y7 r4 O& p* x& }. j
  26.     # mysql 锁
    $ p+ p5 D  v+ r0 G- V6 V  {& }
  27.     mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 " G6 H% t5 C1 J) {0 z. T
  28.     $rs = mysqli_query($conn , 'SELECT id FROM a');
    + R  n* z5 L. m# p8 L# o2 X: `
  29.     $id = mysqli_result($rs, 0, 0); $ X( N0 Y2 d* x5 H$ R$ D& k
  30.     if($id > 0)
    * \5 U2 G/ I2 ]; G
  31.     { 4 L' `9 U3 h, ?2 ?4 H
  32.         --$id; ! a1 _' H/ F+ o2 t9 o  f- u1 d" h
  33.         mysqli_query($conn , 'UPDATE a SET id='.$id);
    * f. k, {7 s' n0 ~% S
  34.     } 1 K* h& S9 N7 _- |5 E' J& O) d

  35. 7 f  c# f! U$ h' f
  36.     # mysql 解锁
    + x7 n2 S$ H3 Z
  37.     mysqli_query($conn , 'UNLOCK TABLES');+ x/ B7 y: I4 w3 U; q4 Q7 B0 w
  38.     //查询解锁后的id值! R1 ?! D: p8 e6 k' b4 X3 g# \$ Y) I
  39.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    : c. ~. [& Z; @0 D
  40.     // while($row = mysqli_fetch_assoc($res))$ \5 d/ m6 D" y! ]! V* [1 V6 {
  41.     // {
    " I  T. a, c+ t$ o1 Y) @" T
  42.     //     $id = $row['id'];
    3 T$ K- ~9 U6 z
  43.     // }
    % T1 v# o; {. Y
  44.     // echo $id;
复制代码
/ Q6 G$ o2 H* i- G2 P

1 Q! e2 e" U1 j6 t

( q+ g$ h- `6 `9 p% K2 y& K
PHP文件锁示例:
  1. /*- O# v" T0 o  k! S$ m( n8 G
  2.     模拟秒杀活动-- 商品100件
    8 B6 _8 c: B5 m6 E
  3.     CREATE TABLE ta+ u& N+ R7 N$ f+ Z$ ^
  4.     (
    8 w3 f/ K8 B+ R, W% @! E. l
  5.         id int comment '模拟100件活动商品的数量'; v8 J" P9 i6 b6 y, P4 F
  6.     );
    ) q4 c8 |5 U: F9 z3 L0 E$ F
  7.     INSERT INTO ta VALUES(100);
    - [$ \* @' ~$ G. W1 ~
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件
    % ?; {, F8 q$ H* ~
  9.      */
    % @( {; x  F4 A2 H
  10.     % H1 Q* g: M4 ]" s
  11.     // 关闭错误报告2 k1 A$ e: z4 I; E1 ~5 i" S0 w
  12.      error_reporting(0);
    : H: @; o. g1 e& n% k: h
  13. ; J0 {" [+ _; p# P! X0 T' s
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址/ P& J8 A( [" H* Y6 k0 ^
  15.     $dbuser = 'root';            // mysql用户名/ s$ M. u2 e! d$ T) z- G
  16.     $dbpass = 'root';          // mysql用户名密码/ t6 u4 W* ^* e3 O. L
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
    7 j9 V6 e$ T4 U: ~" u; }/ o4 j
  18.     if(! $conn ): i$ @* H+ }4 p" A
  19.     {& s8 W' L& G) ~8 x; p& p$ a4 l' {
  20.         die('连接失败: ' . mysqli_error($conn));' d6 @. G; q" G2 {! F& I
  21.     }( l0 f8 [3 c, I1 ]5 ?+ h
  22.     // 设置编码,防止中文乱码4 ?. R3 e2 `( b% s* Z0 S
  23.     mysqli_query($conn , "set names utf8");
    * ^1 x$ [' j( D: T' h" ]( k$ p) ]
  24.     mysqli_select_db( $conn, 'temp' );
    7 t% ]$ V+ w( y6 f7 g1 u
  25. 8 i" @: |; R2 x$ w0 U/ i
  26.     # php中的文件锁
    ( Q4 g1 D" |7 Y' d, q* H6 r
  27.     $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
    ( j! ]2 N) d# w) A3 c8 b
  28.     flock($fp, LOCK_EX);// 排他锁
    4 C, I* m) |$ g" W8 e8 K3 C- a
  29.   j) O. V5 c2 _, k. [  E/ C% {' r; E
  30.     $retval = mysqli_query($conn ,'SELECT id FROM ta'); 7 h4 W3 ~4 }$ T2 E+ n
  31.     while($row = mysqli_fetch_assoc($retval))
    4 v, U6 l* I$ ?5 n. ?& n4 F
  32.     {1 o& |0 u5 W; ]( b& I& N  O) K
  33.         $id =  $row['id'];
    ! j# L. s8 b2 l8 e9 S' L
  34.     }) ~' C  B4 b: y9 b7 K( U
  35.    
    2 ], o( g; @: h0 [5 `4 V, {
  36.     if($id > 0) ( Y* _" |  x% h) ^3 x
  37.     { , E4 t8 [0 [/ ~9 d. S! ]
  38.         --$id; $ h; O* B$ `0 N6 f
  39.         mysqli_query($conn ,'UPDATE ta SET id='.$id); % Y3 j$ O# L. j8 B: S
  40.     }
    - ^4 F4 q- T8 h4 C
  41.     # php的文件锁,释放锁
    3 T8 u4 i9 O+ j) ^/ ^
  42.     flock($fp, LOCK_UN); $ a  V; O+ b8 y
  43.     fclose($fp);
    ! b/ c" D* W2 p, r* G

  44. 9 W4 e0 S' F* c$ [! ]. e
  45.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    , D4 i# z8 |/ g/ z
  46.     // while($row = mysqli_fetch_assoc($res))
    ! `" ^  @5 g& [/ J1 c) }
  47.     // {" E+ a, v9 J0 a, t/ k
  48.     //     $id = $row['id'];8 D4 ^. a+ g2 P* y1 [" Q$ ?
  49.     // }" `( |4 b/ t3 @/ b* X5 F$ J) @
  50.     // echo $id;
复制代码

- v7 l  d, V! v
6 s1 j2 I: _$ r2 g: P, ]2 _
抢券活动实例:
  1. public function envelopeSnatching(){. D& R* z. a) R1 J
  2.         $lingqu = $_POST['type'];
    # t5 V# C# _2 h* `/ F- [9 u
  3.         $uid=session('u_id');//用户id$ P6 |5 a5 \- U7 o: z8 {
  4.         if(!$uid){8 F$ E0 S% C# @) j0 m1 y) o3 c
  5.             $data['msg']='您没登录,请先登录!';
    , Z$ R7 c- B, ]: r
  6.         }else if(date('Y-m-d') != '2017-12-12'){! w6 @' S6 d  w% X6 I
  7.             $data['msg']='不在活动时间内!';
    5 |0 X, f# W/ J7 K2 v
  8.         }else{
    - F" M  R. C: j! p, F1 e1 M( b
  9.             $hours=date('H');//当前小时数# \. T/ p1 m: ?4 k# ]& J
  10.             if($hours > '09' || $hours > '17'){
    : S( g4 O, ]4 u* {1 \
  11. 1 @# p+ o' c' Y3 N, }& L
  12.                 if($lingqu == 1 || $lingqu ==2){//点击10点的, a& b' \7 O1 q1 p) ?$ z/ k
  13.                     if($lingqu == 1){) V1 ~: U) w  U, g7 X  n' w
  14.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过) `% t! C) k* y
  15.                         if($hours > '09'){
    3 V. _, [8 g' d8 N5 H
  16.                             $num=mt_rand(25,28);//优惠券金额+ T/ f! h/ S; Q# Z! B4 q
  17.                             $id=1;( x" b; q" ]8 E( f
  18.                         }; e8 p; ?! f' D$ }
  19.                     }else if($lingqu == 2){
    9 M; K# V6 h% n  z% Y
  20.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();) ?1 Q. F6 i. r) P* @/ D2 H  p" I
  21.                         if($hours > '17'){" P7 T7 D/ u+ Z
  22.                             $num=mt_rand(50,55);//优惠券金额' `6 L2 d) C' L+ w% k8 q0 C
  23.                             $id=2;; d! X1 I5 h6 f- j* k5 S4 l" c1 ]
  24.                         }6 Y2 K" V: {6 w8 W
  25.                     }+ ^' ~8 S' u- A. K1 R% Z5 m
  26.                     if(!$id){
    7 d1 D5 ~& A4 t' y/ @
  27.                         $data['msg']='时间还没到,晚点再来吧。';
    , O! u0 [- {! Q7 ^0 c
  28.                     }else{
    2 x0 h& w7 j$ G
  29.                         if($is_lingqu){9 Z$ g% D) i) K4 X$ y; F6 a& D
  30.                             $data['msg']='你已经领取过了,留点给别人吧!';; r: C% }; x% p- Q/ a
  31.                         }else{6 P5 F3 ^& p4 t% ~( s. N" g
  32.                             //锁表$ l- @. p/ y9 G
  33.                             M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');' \6 @/ n9 S( Y: q; ~& r; q
  34.                             $active=M('active_num')->where(array('id'=>$id))->find();: e: b* b, r9 M/ B5 Z" \2 l0 ~$ o; }+ Y
  35.                             if($active > 0){
    # H0 U( J2 h4 l
  36.                                 //开启事务0 u8 F  [! @8 ^9 q! ^
  37.                                 M()->execute('start transaction');6 f; \! k( c/ F: W. ?
  38.                                 $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
    + `( \) u, J% ?% t/ f& `
  39.                                 $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
    - i. g. F/ P. A: B% V
  40.                                 $members_preferential    =    M('members_preferential');$ @9 B4 _# ^% Y! ]) F9 ~" V. O
  41.                                 //对应投资金额,
      L" j- R7 r9 ^6 Z0 J
  42.                                 $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));* {8 M' r' w& }8 B; X
  43.                                 $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');: n+ X. c  O: T2 ^% N3 s6 i, f
  44.                                 $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间+ M9 b, j4 U3 u$ t1 B/ M
  45.                                 $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
    3 r, r: D3 \: ?# O
  46.                                 if($save && $add && $add2){( i- E" y( U2 q" V
  47.                                     //事务提交
    - E7 J. h! x" Y4 e
  48.                                     M()->execute('commit');; \2 h: b7 ^5 [
  49.                                     $data['msg']='恭喜你领取了'.$num.'元优惠券';
    8 V$ B" Y9 `7 l
  50.                                 }else{; }' [* P1 q- [+ e5 a4 z) F
  51.                                     //回滚1 k& b5 f+ _/ r
  52.                                     M()->execute('rollback');
    ' x0 X! z$ c; W5 c1 N
  53.                                     $data['msg']='未知错误!';
    * Q5 {3 \. ?( o0 ?. q4 j4 g
  54.                                 }
    ( B  x/ |2 y9 H" f" N0 r
  55.                             }else{
    , y( e1 C7 D) T! |7 ~% h" z/ c
  56.                                 $data['msg']='红包已领完,你来晚了!';
    % g* i& a7 _: a3 @+ Y$ O$ T2 Z: B
  57.                             }
    ( K" m0 @$ Z6 u3 b5 u" E+ K2 Q* Y
  58.                             M()->execute('UNLOCK TABLES');  z) f  o9 G) {- P* }( V% I/ h  L
  59.                         }
    & z- e. b8 B  C2 G% `$ ?: I9 C
  60.                     }
    - w: K/ g3 C: V9 N) @/ E) Y1 P! X7 y
  61.                 }else{
    2 b+ A6 k& w  O5 f& n) m
  62.                     $data['msg']='非法操作!';5 D& Y& K  c0 r+ Z" b2 v7 S" p$ e* Z
  63.                 }
    & ]8 p- H, s3 b" F- r
  64.             }else{
    * D6 u. M" n! a6 B: b
  65.                 $data['msg']='还没有到活动时间,请晚点再来哟!!';
    ( F/ u( m# f9 c6 [" w
  66.             }
    9 H1 Z' i7 @4 s$ P1 Y  o9 }  m
  67.         }% v, R- l/ U4 ~5 v5 n( B9 ?# Q
  68.         exit(json_encode($data));
    8 e1 q9 V# V/ Z0 c$ H
  69.     }
复制代码
8 {# x$ g4 G. q% ^/ o

! W1 y9 F5 n9 }4 K7 y) ]+ w




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