您尚未登录,请登录后浏览更多内容! 登录 | 立即注册

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 3945|回复: 0
打印 上一主题 下一主题

[php学习资料] MySQL(表锁)、PHP(文件锁)锁机制及应用场景

[复制链接]
跳转到指定楼层
楼主
发表于 2022-3-17 15:53:00 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量
  1. C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php   // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码

  j  }. i' Z$ R' i- \6 N
Mysql中的锁语法:4 X) b# G. U' l! K  s" L% T9 @
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】! ^& K% ~4 y) ^
UNLOCK TABLES  【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表9 ^' w9 V) I5 ~4 ~  u+ D
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
$ K5 J* S/ |* R注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :1 e% o! H  h- t, Q, r( ?
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。$ c" s. o9 c! l! W8 q
测试时,有个文件就行,叫什么名无所谓
总结:
$ r- J3 c; v' [1 d4 p* Z" Q项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:
; f$ ^8 W0 l! k0 d7 W1. 高并发下单时,减库存量时要加锁
: P/ ?( Q" L3 p, F5 t2. 高并发抢单、抢票时要使用
Mysql锁示例:
  1.    /*
    # Q. i2 _: w& q6 {9 u9 }- {
  2.     模拟秒杀活动-- 商品100件
    ! _% a# p* g4 }) A+ g1 W
  3.     CREATE TABLE ta+ I+ D8 }( Z' t/ g
  4.     (
    , e5 B* K( s# b7 g
  5.         id int comment '模拟100件活动商品的数量'  W  l+ ~! k- W9 R/ {: ^
  6.     );
    0 `+ M1 U" t1 Q7 U4 ?+ s9 |! v
  7.     INSERT INTO ta VALUES(100);; m6 |6 c+ b( X- Z$ |$ t
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件$ M5 c3 U# G. D# P4 J( r
  9.      */ ' p7 r- X6 r3 G( h% i1 U
  10.     % E6 x9 ^( Y2 V% W: U
  11.     // 关闭错误报告! M2 d. i" a2 J7 j+ i
  12.      error_reporting(0); 7 k( l; E- V5 Q% }( o2 }7 L, b6 O) q

  13. : f+ s$ B  o, s; n9 P
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址- R+ v2 A$ ~0 }' m
  15.     $dbuser = 'root';            // mysql用户名
    8 _% B( H9 P% I
  16.     $dbpass = 'root';          // mysql用户名密码
    % X: p) Q2 e2 t1 }) L1 u
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);# d( [8 L. k5 B: K; H) U
  18.     if(! $conn )( z5 w  Q" ]' L# j( x6 a
  19.     {
    & Z- W3 w% g* M/ M: q5 M
  20.         die('连接失败: ' . mysqli_error($conn));
    - Y" j) W/ @+ U8 F% k
  21.     }8 V6 L- R# m5 s) ?
  22.     // 设置编码,防止中文乱码
    7 _+ H; ]+ T) b0 e" ~8 c6 S
  23.     mysqli_query($conn , "set names utf8");
    + y+ E3 A4 m* V# Y+ R5 c' n, v5 `
  24.     mysqli_select_db( $conn, 'temp' ); $ ~3 ~0 V. g9 L

  25. 5 H" D& z: V  V8 Z
  26.     # mysql 锁 4 }% N5 s" ~1 P2 k. x, g
  27.     mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
    + j4 l0 p/ b& A& F8 \9 K6 M4 U: e
  28.     $rs = mysqli_query($conn , 'SELECT id FROM a');
    * d0 m5 K4 h- Z
  29.     $id = mysqli_result($rs, 0, 0); - R! A' Q' T  y. ~: C
  30.     if($id > 0) 9 X+ u; g( ^& i- h- @
  31.     { 2 H3 Z" H7 r! t
  32.         --$id; 2 U" W) v  b( Y! t/ Q. }1 K4 Z
  33.         mysqli_query($conn , 'UPDATE a SET id='.$id); 7 W4 P+ A+ M" Q1 n
  34.     }
    ( V8 z( _# l- a: u5 T4 k& K

  35. ( R4 m" w& ~: X
  36.     # mysql 解锁
    - f- \: Q: Z7 l
  37.     mysqli_query($conn , 'UNLOCK TABLES');( x% K, {. h) R: e3 }
  38.     //查询解锁后的id值6 U8 ?: \) L" i- V7 }) w) e
  39.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    / v& n$ W0 a) G% ?
  40.     // while($row = mysqli_fetch_assoc($res))
    : r7 D$ ^9 s4 D. W" ]/ O
  41.     // {
    7 A7 f% g  X2 B0 L) \; {
  42.     //     $id = $row['id'];
    ) p6 ]( V4 b( h' `2 h
  43.     // }
    3 |) E1 G  j# S7 L0 T" @
  44.     // echo $id;
复制代码
6 S* k9 z! n" Z/ {1 w9 c/ @" Z: s

- |, y" s+ O4 |( z& c
* q1 N+ Y+ ?9 ^/ j+ @# h
PHP文件锁示例:
  1. /*
    $ t7 F6 c. y" R# R3 W( K0 ~- O
  2.     模拟秒杀活动-- 商品100件
    1 t( j! O# f" J% ~
  3.     CREATE TABLE ta
    9 x6 _% v8 k; t! B5 ~: d
  4.     (
    ! m$ f# a! e! B2 {! N5 ~/ w
  5.         id int comment '模拟100件活动商品的数量'
    / @( X! P) G! U( o7 J9 F) _: ~
  6.     );
    7 Z4 j1 Z  Z+ V1 |2 S
  7.     INSERT INTO ta VALUES(100);" p! O3 R$ J" m7 @% T+ G8 ~
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件* u4 s+ p/ H' o+ `; I0 }/ p: B5 R' F! S
  9.      */
    ( I  a. X4 F% U; B# b
  10.    
    6 j4 ]& s7 o- t5 u! F# V
  11.     // 关闭错误报告
    : I% U. S: A+ R: l
  12.      error_reporting(0);
    - g( y7 A! z* R& Y, N

  13.   a; W, z3 Q5 H1 g" N% x! a
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址- \. a% s3 G% \4 Q2 r
  15.     $dbuser = 'root';            // mysql用户名) z( |1 H# w# ~
  16.     $dbpass = 'root';          // mysql用户名密码# a' {  X0 H! k! U- }5 E1 u
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
    5 u4 [4 c) y: x) L( z, A
  18.     if(! $conn )
    ) n9 h9 D  W/ N  ^$ s# S/ p
  19.     {
    * O% K2 x; T. I7 v9 }
  20.         die('连接失败: ' . mysqli_error($conn));, ]+ E+ F  s& X" i
  21.     }
    * @) Y0 b" x# w3 q. g) l
  22.     // 设置编码,防止中文乱码
    . N- i$ A. n* g( I
  23.     mysqli_query($conn , "set names utf8");
    6 a* S3 ]; T: V7 @* k8 C: ?
  24.     mysqli_select_db( $conn, 'temp' );
    3 V1 n: L6 c; H8 R

  25. # F1 R" O# f0 [2 F* b
  26.     # php中的文件锁
      C) ?( C! c1 s. ^$ f
  27.     $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
    % U2 Q: F5 |  {% ^
  28.     flock($fp, LOCK_EX);// 排他锁 9 h; [( R$ M' F4 G' x
  29. ' H' B2 W/ Z1 l
  30.     $retval = mysqli_query($conn ,'SELECT id FROM ta');
    - ?8 s, S. A7 _$ a# I4 y
  31.     while($row = mysqli_fetch_assoc($retval))! g9 t! |8 Z8 q' \% X6 X& I. D
  32.     {1 B: O0 l" @9 S. W# E) T0 x
  33.         $id =  $row['id'];
    " z( G2 r5 U4 A  u
  34.     }2 p' q) C1 e7 ]9 E, t, r
  35.    
    / K, ~. c2 L# L0 m2 J
  36.     if($id > 0)
    6 [7 Y8 A' ^- C2 \
  37.     { 5 o: y5 J5 I4 u& x- S+ U3 A! Q( y1 {
  38.         --$id; / c& p; ~1 x% K) W
  39.         mysqli_query($conn ,'UPDATE ta SET id='.$id);
    + u" u; ~2 f4 o# t2 |- v
  40.     } ' o& J0 D; m, K9 h
  41.     # php的文件锁,释放锁 ! M$ U% G5 N. p1 Z, J
  42.     flock($fp, LOCK_UN);
    6 X: ?: s2 y( p
  43.     fclose($fp);* |. }6 q- ~( X* G9 R( ~/ R2 w* H
  44. 2 K& L' t3 ^0 D* c3 w  i: @
  45.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    & c  w5 G8 O! @6 J
  46.     // while($row = mysqli_fetch_assoc($res))
      \4 j, t: ^9 b
  47.     // {
    6 r; X9 ^1 S9 a9 O) b
  48.     //     $id = $row['id'];
      T" @/ s4 w( B' b7 [% K
  49.     // }" l9 x. V: \. l% C6 d
  50.     // echo $id;
复制代码
- ^9 h, e5 A( |' |/ q* U
: q' d/ K$ I; {+ v& I7 D4 c
抢券活动实例:
  1. public function envelopeSnatching(){
    * ^# w7 B" P4 H2 k' o4 J- }
  2.         $lingqu = $_POST['type'];
    ; }4 Q) T7 S! M; ~
  3.         $uid=session('u_id');//用户id
    ) f* O( u2 \$ R3 e# e
  4.         if(!$uid){
    4 g% K5 d3 L: T+ f8 Q
  5.             $data['msg']='您没登录,请先登录!';1 G! `* A8 ?# q
  6.         }else if(date('Y-m-d') != '2017-12-12'){
    / U2 i7 ?- a7 v& d: B& C
  7.             $data['msg']='不在活动时间内!';# {! M  ]( {% ~* |# q8 G0 s
  8.         }else{6 m6 D2 j! n$ y: i- d$ y5 B2 E+ A
  9.             $hours=date('H');//当前小时数6 D" r7 W: r3 k: {- y) x
  10.             if($hours > '09' || $hours > '17'){5 R9 h6 V, i1 G7 I8 X( ~: d& g

  11. 5 C8 O! K( h: L& v. e" D
  12.                 if($lingqu == 1 || $lingqu ==2){//点击10点的, \7 P. C, h) B; a/ H
  13.                     if($lingqu == 1){
    : U4 q9 _' |/ k" T/ \
  14.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过: I& p+ G/ g) |% C, O$ ^
  15.                         if($hours > '09'){6 k! u" K( p/ M* `
  16.                             $num=mt_rand(25,28);//优惠券金额
    5 K: e/ f( I; ~: J
  17.                             $id=1;  F& L- O0 w# D" _# t: Z3 t! z3 O
  18.                         }  M7 q# M" G: J) N2 ~
  19.                     }else if($lingqu == 2){
    ' h0 p4 T% O/ Z; d
  20.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();2 e9 b/ R' E' [9 u3 q. |7 t7 w
  21.                         if($hours > '17'){
    0 Y" l7 v4 s1 p3 l3 T! S
  22.                             $num=mt_rand(50,55);//优惠券金额
    ' i# a$ M( ~/ ~- p: r& j
  23.                             $id=2;
    # J8 H1 T" O; O6 t9 ~
  24.                         }9 p, {- l/ @( A5 j
  25.                     }+ p, [# N. s3 H1 K
  26.                     if(!$id){# t" E, r1 k9 \% m: J
  27.                         $data['msg']='时间还没到,晚点再来吧。';# N$ k4 L( {$ A* {0 w
  28.                     }else{. h! h- v4 [, M1 ]! x
  29.                         if($is_lingqu){
    5 ~: E( u! k9 K& w$ ?2 i# c; M
  30.                             $data['msg']='你已经领取过了,留点给别人吧!';  v4 W3 I% Z5 w( P: _' @
  31.                         }else{
    - G! m# |" M4 E
  32.                             //锁表
    3 Y0 \6 s; A* p' f; Z
  33.                             M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
    7 e1 D; d2 e  ^0 a: j3 l
  34.                             $active=M('active_num')->where(array('id'=>$id))->find();; a; S$ O  u- P' ^
  35.                             if($active > 0){
    - h/ z" S5 v/ m+ N8 i9 ?2 X) @3 z
  36.                                 //开启事务
    1 ]$ d) Q7 l  L# \
  37.                                 M()->execute('start transaction');
    ' k; F, _6 ?3 p7 A" b. H
  38.                                 $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));5 D7 _4 ], D, O" q/ }+ r
  39.                                 $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));6 q' Q2 ?  Y( Q/ s& t, m/ c
  40.                                 $members_preferential    =    M('members_preferential');
    ' x: M& v4 ~0 b) f5 u* Q
  41.                                 //对应投资金额,) B% `6 ?( m& _* S( q% Y1 A
  42.                                 $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));5 K: A5 l/ v% Z/ t' L* C% N
  43.                                 $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
    . _- M$ `/ F! R; K1 n; ?3 O
  44.                                 $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间/ K0 w0 Y/ I! G$ {$ D" t
  45.                                 $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
    6 A* U5 ~* m% ~" P5 T, G! U# ]
  46.                                 if($save && $add && $add2){9 a! R! I  k' d& a
  47.                                     //事务提交
    3 w# N0 L& D2 {6 \0 d. o/ w
  48.                                     M()->execute('commit');
    ! \9 W4 r+ `8 o; _" o4 l5 ]
  49.                                     $data['msg']='恭喜你领取了'.$num.'元优惠券';
    7 N: P) c9 q$ q
  50.                                 }else{6 h; D0 ~, t* _( o( D
  51.                                     //回滚" B! \: J) X2 Q0 L( n* H  J' [
  52.                                     M()->execute('rollback');$ F* s' k; \, Q, W8 X
  53.                                     $data['msg']='未知错误!';
    * _/ [# u0 u0 O3 n, n- S% V- c
  54.                                 }
    9 ^  x+ ]1 s6 a+ U
  55.                             }else{" U: p8 y3 g, }
  56.                                 $data['msg']='红包已领完,你来晚了!';
    ; `( F$ S) h3 {; g
  57.                             }9 W8 Z' y) m7 h
  58.                             M()->execute('UNLOCK TABLES');
    ( S+ v; K! y( x7 C0 D  q, ^
  59.                         }4 m, N' P. c. f: g6 j+ t( L
  60.                     }. y/ @' C( _: ]5 X3 l' k
  61.                 }else{
    * A2 K4 z5 g; K" w) E: v& ]. S
  62.                     $data['msg']='非法操作!';7 u8 |& j1 N2 c4 `- Y
  63.                 }6 Q2 g1 X9 j4 d+ Z% |/ @) H
  64.             }else{$ f( j" V0 Q& y- J4 z
  65.                 $data['msg']='还没有到活动时间,请晚点再来哟!!';. h$ N* ~) i' L# ]2 S
  66.             }
    ) |$ y2 j; D2 n5 y9 i
  67.         }
    : t9 G: H9 f2 G, C5 V/ Q, E2 ^
  68.         exit(json_encode($data));4 e1 R8 k. ], V% g8 P
  69.     }
复制代码
* `0 i6 M8 H( N$ m  S5 X. w
- l! I' x' y7 D3 B
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-17 18:43 , Processed in 0.140681 second(s), 19 queries .

Copyright © 2001-2024 Powered by cncml! X3.2. Theme By cncml!