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

" I& e6 r9 c; s. w( _( A
Mysql中的锁语法:
( A% b" Z( [  WLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
8 x: E2 K- _8 C/ GUNLOCK TABLES  【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
8 G( |6 P; }* Y' O& u. W" XWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞% D+ c7 ]- e4 y( G% _7 Q
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :5 U; [$ I! Z- B1 g8 [" F& q
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。- p; Y/ r$ x. b& X, K3 Z- }% D) ?. }
测试时,有个文件就行,叫什么名无所谓
总结:$ P  w# R! {; w; E! v: x
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:) s- v3 {* t6 T  Y+ I) a
1. 高并发下单时,减库存量时要加锁! ]- M) _) C. \( Y7 E; A
2. 高并发抢单、抢票时要使用
Mysql锁示例:
  1.    /*$ y; P3 ?6 U4 ~5 I" f* Z
  2.     模拟秒杀活动-- 商品100件. n1 @9 U0 J) x' b6 c1 E
  3.     CREATE TABLE ta# o5 C% f( w* U; t" A( Y
  4.     (& {1 A: D( U5 {7 v. ?. V
  5.         id int comment '模拟100件活动商品的数量'
    4 t! I6 D! K% W0 Z  A3 C7 D# y) Q6 j
  6.     );. G4 p  Y* |/ a: f9 D
  7.     INSERT INTO ta VALUES(100);
      N) D& f) u2 T1 s1 L
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件
    % ~; e5 H5 B: t! ~3 A8 a" X
  9.      */ ! x: G8 n5 e  _6 P  M
  10.    
    6 Y9 T1 x2 b" H& b; L, B6 }
  11.     // 关闭错误报告
    7 W# J$ S9 j4 H- L/ v
  12.      error_reporting(0);
    4 w6 h' ?$ N; t  b
  13. 7 j& |" S# b6 L, O8 }+ @
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址
    ) N: `3 N6 `! `
  15.     $dbuser = 'root';            // mysql用户名
    9 w# p" u: b6 b$ \4 y
  16.     $dbpass = 'root';          // mysql用户名密码
    3 r, E3 |5 e9 M# G6 E! c) ~
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);; F4 t4 o% M, C  v, G8 u- k
  18.     if(! $conn )
    3 v1 L. }! [' _* A1 u/ [5 ]- b1 W8 M
  19.     {
    ' |8 x2 J: T; l' R1 M8 Z6 i
  20.         die('连接失败: ' . mysqli_error($conn));. \. }, i6 k) V1 Y0 ^
  21.     }
    5 J- s0 }- t# }' l; V( T: g" V" j4 `
  22.     // 设置编码,防止中文乱码
    ! N9 _5 j! t! T, X3 {- Z; Q
  23.     mysqli_query($conn , "set names utf8");' L5 e5 m- `+ a% r1 |, l6 z
  24.     mysqli_select_db( $conn, 'temp' ); ) H+ b9 J) O% \) ?
  25. ( \  \3 |! x$ ]
  26.     # mysql 锁
    5 G$ y: _$ X9 b( ~4 M+ n6 |
  27.     mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 * b; G8 \  r" o" Y5 y" U6 f% n
  28.     $rs = mysqli_query($conn , 'SELECT id FROM a'); ( F7 D3 f# J& F3 ~) U
  29.     $id = mysqli_result($rs, 0, 0); / r+ s: _" q3 B
  30.     if($id > 0) / `3 Q8 _6 X4 h
  31.     { # u" J6 i" J/ d& \
  32.         --$id;
    $ B, x) b8 ?) l  `5 p, l5 F
  33.         mysqli_query($conn , 'UPDATE a SET id='.$id);
    2 x# z5 |- P. n6 \* [, J
  34.     } 8 p) Y# ?8 U# ~! o: P  S" K# C

  35. . E' [. \$ ]2 F9 V( K# p- A/ J' y
  36.     # mysql 解锁
    * w' I2 Z6 W" `2 R& @
  37.     mysqli_query($conn , 'UNLOCK TABLES');1 u( d7 ?) B+ n
  38.     //查询解锁后的id值
    + f' o1 k5 V2 C8 e! J
  39.     // $res = mysqli_query($conn , 'SELECT id FROM ta');' _9 g0 E$ H+ y0 ~
  40.     // while($row = mysqli_fetch_assoc($res))
    ( [( B" I* z% a
  41.     // {1 d( G# `- |6 c0 a6 D, L
  42.     //     $id = $row['id'];. l, |' X) A+ t
  43.     // }
    & v# r" `# R: C4 k0 \- p) V, g
  44.     // echo $id;
复制代码
2 \. J! j3 D/ o) u
) Z6 p! d' c! C2 o) ?" p

- d" ^  O, R% ]8 K, R
PHP文件锁示例:
  1. /*
    # T  `4 z) R# N$ R
  2.     模拟秒杀活动-- 商品100件
    $ T0 r  Z5 \8 N
  3.     CREATE TABLE ta; F# L2 X/ p; Q# E: X
  4.     ($ @  T4 D0 Q5 C
  5.         id int comment '模拟100件活动商品的数量'
    8 U( G2 b  ]" @6 c6 b0 L4 P
  6.     );( L! t& |; |: C  ?; S, B5 U: d2 ]/ q
  7.     INSERT INTO ta VALUES(100);' H7 c0 Y5 ^, L* ?7 _1 F
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件% K3 |5 T( ]' m' O( ~& g
  9.      */
    4 C6 j$ c0 a5 }: q9 G  O1 {
  10.    
    / ?) I- T* J( A& |4 |5 g
  11.     // 关闭错误报告
    * d  _  P7 I$ b" o& o
  12.      error_reporting(0);
    & V9 o% A1 L" A/ T

  13. 6 F( H; X3 M! {0 Q' {
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址. C/ q. N! _+ c4 [6 [; G! u
  15.     $dbuser = 'root';            // mysql用户名
    5 C& f, q9 \, ?: ~1 M
  16.     $dbpass = 'root';          // mysql用户名密码" s% G0 |8 X3 e% C) ]' V+ K2 C$ ^
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
    / F2 h4 [' w6 K
  18.     if(! $conn )3 e3 U+ p6 a: b5 g  L
  19.     {, Y( R0 H) }  I
  20.         die('连接失败: ' . mysqli_error($conn));, X% [/ a- Z' k! _% O8 s
  21.     }+ N% G/ t0 p/ v2 n
  22.     // 设置编码,防止中文乱码
    ' r6 D! S. U' }! q& O" m
  23.     mysqli_query($conn , "set names utf8");
    . X& K; B, R& ?
  24.     mysqli_select_db( $conn, 'temp' );
    ) R0 H) T8 e* G- b" r  L

  25.   J1 L2 ?$ d! M. M+ O5 L
  26.     # php中的文件锁 & _4 R' k  ?! ?% m
  27.     $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 , ]* g2 A2 k0 ~
  28.     flock($fp, LOCK_EX);// 排他锁 7 n0 E0 u# B2 r# ~1 n  Y: {

  29. . z6 L: \4 F4 {+ q) X
  30.     $retval = mysqli_query($conn ,'SELECT id FROM ta');
    , ]; @0 R+ L. a' `" X* ]) z
  31.     while($row = mysqli_fetch_assoc($retval))
    1 c4 `) ^4 |9 V, V
  32.     {+ l# ^3 S5 X# z* U# F8 R
  33.         $id =  $row['id'];
    0 P& Y# ~. X+ X# T0 t: {+ V
  34.     }4 x4 y- V  F7 V& ?
  35.    
    3 b4 y! n" ?( `
  36.     if($id > 0)
    , z! o6 k5 [& ~- b$ Q
  37.     { % Q* S: H: I' U) ?: }! F! C4 U
  38.         --$id; # u& h7 _, J/ \+ n& C6 ]+ K5 a
  39.         mysqli_query($conn ,'UPDATE ta SET id='.$id);
    0 \' |' @' f+ v9 W( U
  40.     }
    $ ^* @9 r9 F. @. ]' o5 X$ ~1 D' U
  41.     # php的文件锁,释放锁 5 X" Z. t9 `+ f9 B5 W- Z$ C' N4 `
  42.     flock($fp, LOCK_UN);
    6 C/ r( f3 P' ]" o2 o/ k
  43.     fclose($fp);
    2 [4 A7 M  j1 u- \: J. K

  44. ' ^& [1 X# F2 x7 R, S4 E
  45.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    $ B& G5 `" p- @- ?; @
  46.     // while($row = mysqli_fetch_assoc($res))& O* l* D( F# U# y% m* m
  47.     // {
    9 o) |( _( b4 w, B* z
  48.     //     $id = $row['id'];
    4 R+ ^4 O; N1 n2 V5 r. e
  49.     // }
    2 G. G: T7 v8 _' C7 D
  50.     // echo $id;
复制代码
- h: i- j0 E, w1 [4 P. I1 d
3 @9 ?' j9 r$ ]( c& p# S9 [  c2 \1 K
抢券活动实例:
  1. public function envelopeSnatching(){
    + z! C) W7 H% D5 b) O. Y4 `
  2.         $lingqu = $_POST['type'];
    $ `, @3 t7 n: O6 T1 \
  3.         $uid=session('u_id');//用户id
    $ k3 Z) W+ ~6 j: \) l
  4.         if(!$uid){
    & B3 s  j* n7 C" g
  5.             $data['msg']='您没登录,请先登录!';
    3 M* [( C; d8 g" i: H
  6.         }else if(date('Y-m-d') != '2017-12-12'){+ J2 ^: \; |5 U4 N: x( `
  7.             $data['msg']='不在活动时间内!';/ S# Y" W1 Y( e! a- U4 ?5 z
  8.         }else{- F6 r$ W1 N" g6 I4 b' g* |
  9.             $hours=date('H');//当前小时数! |3 Z5 ^$ {/ H1 i, J4 i
  10.             if($hours > '09' || $hours > '17'){
    0 H% l& j8 U) o/ e3 w

  11. 7 g* t1 |, R# P& y& @- w
  12.                 if($lingqu == 1 || $lingqu ==2){//点击10点的
    ; v: K9 i5 l* ]( R7 G, M6 E
  13.                     if($lingqu == 1){3 {; z$ O( @1 S0 e/ }' ?. f
  14.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过0 X, e  t; V) p. `9 e! E9 |
  15.                         if($hours > '09'){
    6 m4 I! b* c# _7 A5 d' m( Y
  16.                             $num=mt_rand(25,28);//优惠券金额: L, c7 P# u7 [. y! H9 W- V
  17.                             $id=1;
    2 P9 r  U( c6 K3 b
  18.                         }
    " `7 Z; H" |. P9 a, ~* {  y, ~4 o) d
  19.                     }else if($lingqu == 2){# V$ r+ g' n$ x, h
  20.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
    ! g& e$ J& S% |: X; b2 T  b4 O; r
  21.                         if($hours > '17'){
    # T7 }; ?& b7 Y, ]0 y
  22.                             $num=mt_rand(50,55);//优惠券金额
    2 y  }, N( @: T8 X4 ]2 ^# u% V
  23.                             $id=2;1 C; m8 {4 v& l2 [: I: P
  24.                         }  v7 }0 R. k# O# q$ j9 n/ C
  25.                     }
    ! P$ G5 w6 D! J# ?
  26.                     if(!$id){" ?3 c1 V) C4 K$ P( k. Z. }; d
  27.                         $data['msg']='时间还没到,晚点再来吧。';
    . K7 [& Y, a: ~; X+ |+ `' z2 W
  28.                     }else{1 `, Z9 o0 }, l. E8 s& D1 W4 l, X
  29.                         if($is_lingqu){, Z  z% y/ N% F1 E6 l& h7 R
  30.                             $data['msg']='你已经领取过了,留点给别人吧!';
    " y. f) }7 s- K3 m
  31.                         }else{
    # @" s, Y$ h; G
  32.                             //锁表: v# `0 n6 ^! L/ u( g" z2 K: B
  33.                             M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
    9 @: \. k* s/ O* B
  34.                             $active=M('active_num')->where(array('id'=>$id))->find();1 _  c  t2 G! u8 L& Y# n
  35.                             if($active > 0){
    . G$ L% x3 ^0 R+ v7 K
  36.                                 //开启事务
    , i2 o  _# f+ w( H' V. }2 |
  37.                                 M()->execute('start transaction');. Y) Y+ ~- [' E
  38.                                 $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));# }1 U* q6 s5 H2 {, d# Q. K: d4 q
  39.                                 $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
    + C" G- |3 j, \7 s
  40.                                 $members_preferential    =    M('members_preferential');( N& x9 t( H/ M: J
  41.                                 //对应投资金额,
    & L5 H# a. `. N1 F( |4 `: |; a
  42.                                 $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
    " X' a: ]7 R- m8 E' v* ^
  43.                                 $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
    5 Z  V1 W/ \: J1 Q8 l
  44.                                 $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间: Z. [/ i  J( c9 y
  45.                                 $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券1 [4 X+ N; ]% y  p" Z, U- I" C- z  R
  46.                                 if($save && $add && $add2){0 m( m/ D7 ~6 K1 i/ _# c
  47.                                     //事务提交3 k" H" I+ B) H, E4 e  a% j
  48.                                     M()->execute('commit');1 }; h3 x0 H3 Z8 Q+ o6 u' b
  49.                                     $data['msg']='恭喜你领取了'.$num.'元优惠券';( ]& z- w9 j& D" L- u5 {
  50.                                 }else{4 n; Q; C4 f) p2 T+ G
  51.                                     //回滚
    + W! n4 O! Z" D5 `( u
  52.                                     M()->execute('rollback');  O( B3 ]% a3 s: P
  53.                                     $data['msg']='未知错误!';
    8 J7 @( H* \9 x' P9 ?
  54.                                 }
    2 g9 X1 _& t$ J0 @* _9 k
  55.                             }else{6 J  a, S7 n1 B
  56.                                 $data['msg']='红包已领完,你来晚了!';8 p3 m& [/ F5 v. u
  57.                             }9 k* w$ r" X, g* x: T3 ]
  58.                             M()->execute('UNLOCK TABLES');
    5 H0 r# H8 X6 V+ s+ z/ _
  59.                         }: n9 X  u9 Z, G' l
  60.                     }- O7 D1 N/ H4 Z- G5 I, @5 K
  61.                 }else{' J0 y! j7 r' y, Z- q
  62.                     $data['msg']='非法操作!';0 l' s. E6 c: `* z, T: h
  63.                 }
    # C5 Y9 q! y0 ]+ e
  64.             }else{( U3 M% Q4 D2 y2 }: d
  65.                 $data['msg']='还没有到活动时间,请晚点再来哟!!';1 o) e/ @) K; P, [5 c$ n
  66.             }# @- c2 ?: j$ V" o
  67.         }
    " }5 _  m* C* v! t$ _2 g
  68.         exit(json_encode($data));
    " I( o4 S( r2 ?5 Q% v0 W$ q
  69.     }
复制代码

" Q) p7 V2 x7 V' P3 U
, ?& ^' v, b7 Q/ M




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