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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 8892|回复: 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://请求的脚本
复制代码
! g( W- w3 a6 C9 v2 p- ^) e- h0 P
Mysql中的锁语法:
! S+ C" r3 k& I7 {0 k* D4 hLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】8 F: @8 L6 v' Z& [' y
UNLOCK TABLES  【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表7 W! Z7 _: R% r# x: p
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞- o+ C. w1 G1 m3 C( Y; O% J
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :
3 U" N' Z2 f) _文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。: P# I# v- ?; V5 W9 f; v
测试时,有个文件就行,叫什么名无所谓
总结:9 I5 v4 D; u( `, w. j: B/ x, R
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:  ]0 [! t5 Y( j- n0 X2 L# L
1. 高并发下单时,减库存量时要加锁
% r, I. I/ Y5 S3 I% ~% }2. 高并发抢单、抢票时要使用
Mysql锁示例:
  1.    /*" ^; [- `2 U& e2 m  X
  2.     模拟秒杀活动-- 商品100件
    2 s0 m7 c( D- c  _9 n
  3.     CREATE TABLE ta
    1 k. F2 m. I: O9 G" V
  4.     (
    / o+ h3 {2 \1 Q9 @' ?
  5.         id int comment '模拟100件活动商品的数量'* ~) M6 s. q; \1 T
  6.     );+ S1 A. X9 S5 J0 ~# {, k
  7.     INSERT INTO ta VALUES(100);
    + r6 p8 K/ W4 j# R6 J
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件
    # p$ T) K- l0 i4 F: I9 R& m2 z
  9.      */ ! R& H: p: X1 V8 f
  10.     & Z" q5 ^! i9 U9 ~# G
  11.     // 关闭错误报告
    9 A# v- @# W; b- M$ \  T
  12.      error_reporting(0); $ Y: {5 p7 F0 ~
  13. ! @+ @8 o! i" v1 M; l5 l
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址
    2 h( j. N9 d3 A9 _, B+ e8 a
  15.     $dbuser = 'root';            // mysql用户名7 s; h( @, e" T) |2 ~
  16.     $dbpass = 'root';          // mysql用户名密码. C( n2 Y/ z# A2 ~2 u  Q
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);0 T- b  |6 m( j* t$ I, v: C% ?
  18.     if(! $conn )
    7 p5 h4 L, h' b6 ^
  19.     {
    ( @8 {1 |' `7 s: W. U
  20.         die('连接失败: ' . mysqli_error($conn));
    7 B& F$ o2 y" z) A3 L1 P$ b4 g
  21.     }& f) f! K- t  t2 ?1 O% x5 ]) I
  22.     // 设置编码,防止中文乱码
    4 G, h' Y5 @+ {' e
  23.     mysqli_query($conn , "set names utf8");( v+ W% A! d3 {- u4 P: ?! {
  24.     mysqli_select_db( $conn, 'temp' );
    ( @8 V5 z1 \- [

  25. : D' L9 p) R' R! @+ ~2 ^; \; ^3 y
  26.     # mysql 锁
    / B3 u) D2 r8 i5 o2 e7 i4 W3 b
  27.     mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
    8 p* N) R5 u2 J- _' W
  28.     $rs = mysqli_query($conn , 'SELECT id FROM a'); 6 h8 L; E% {2 R5 B& m
  29.     $id = mysqli_result($rs, 0, 0); ' }+ z; b1 R* |* N/ [- i6 V
  30.     if($id > 0)
    + V, X& c, V8 O, R
  31.     {
    : H8 w1 [4 e8 P3 O3 D- ~( l
  32.         --$id; ! `3 n% n: ?" z( P1 q( x% K
  33.         mysqli_query($conn , 'UPDATE a SET id='.$id);   c% }) h- [1 I  N) W; H
  34.     } ! ]' {1 }; t' T2 D; m8 U

  35. : c' r# W5 S" i
  36.     # mysql 解锁 # a) B1 r4 N# ?% `
  37.     mysqli_query($conn , 'UNLOCK TABLES');
    3 a% u. g  X) ~- i) j
  38.     //查询解锁后的id值  V& S3 T! W, Y0 s
  39.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    $ {; I- o9 c- Z. S5 q
  40.     // while($row = mysqli_fetch_assoc($res))% L5 C( t1 S( w- V3 o$ P
  41.     // {
    1 k# S, D6 {* A% \& H$ n
  42.     //     $id = $row['id'];; T  m- @7 G- K+ r6 Q
  43.     // }
    % P0 |- d' V$ l$ t; Y
  44.     // echo $id;
复制代码

( w( W4 Q* P# G' ~$ o- o5 c
4 K, m- a: ?& ?9 Q0 H

+ G2 A6 C6 n) O# K( g
PHP文件锁示例:
  1. /*1 i4 z- g# U+ {" b. s' G( N( o. }
  2.     模拟秒杀活动-- 商品100件
    - e/ z3 Y! }3 E% w
  3.     CREATE TABLE ta
    8 m, [8 n; Y9 o) z
  4.     (
    + z: [3 |( {" q8 R5 O
  5.         id int comment '模拟100件活动商品的数量'
    2 u' V* P: t4 y3 p
  6.     );9 s. u: k1 N* F4 H
  7.     INSERT INTO ta VALUES(100);
    ( P; V; c: a- j# b2 k( q# ]
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件# H& X5 E: |6 y* @3 p4 }
  9.      */
    ; \; Y' i0 v' I/ x) i
  10.     ; _- L8 A0 C& d  ?% J4 \( W& g
  11.     // 关闭错误报告8 c- l* t% L1 M
  12.      error_reporting(0);
    ) S( z& Y9 k' S$ G1 U
  13. ' O2 I$ i2 I( ~1 r
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址5 _$ S7 M' x" e* r- K
  15.     $dbuser = 'root';            // mysql用户名. g, z4 `% r) }6 s; l# L& p
  16.     $dbpass = 'root';          // mysql用户名密码$ u2 d7 l  T; B9 N4 U4 S0 g
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);2 m6 s3 ~1 x. c+ R: ~2 ]1 `
  18.     if(! $conn )1 L- i: _' z, O3 l' v
  19.     {
    : ]& R' d* O4 m4 w# o, Z
  20.         die('连接失败: ' . mysqli_error($conn));, g  Z, r- ]2 U' ^+ v
  21.     }* d+ x0 S+ Y: r5 g  V- Y" X
  22.     // 设置编码,防止中文乱码
    # c# K2 r% c5 W
  23.     mysqli_query($conn , "set names utf8");9 W  ~/ M' B# n1 Q( n; {* \9 g
  24.     mysqli_select_db( $conn, 'temp' ); 6 W1 G# N1 S% |/ p
  25. 3 h9 ?& ]6 c. R8 S6 J7 @
  26.     # php中的文件锁 % ]* d9 c( w+ e; u. j7 Y
  27.     $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
    ( _5 i1 Y9 V. a7 K; y% u) m  I
  28.     flock($fp, LOCK_EX);// 排他锁
    ! {  [0 {/ U9 J& {; ^8 K

  29. ! V/ n2 c& b1 S: P" |6 Q; {7 w+ j
  30.     $retval = mysqli_query($conn ,'SELECT id FROM ta'); 0 F  [; H& T5 L# a0 x7 T: ]
  31.     while($row = mysqli_fetch_assoc($retval))0 Z. v: v6 M) \3 B4 B% s6 `. z; b* X
  32.     {' Y! R4 d3 i8 e
  33.         $id =  $row['id'];
    1 T& v6 u% t, T5 J
  34.     }. Y4 I) \; g7 Q7 ~
  35.    
    $ `" ]1 ^$ ~) y! D+ k' y+ Q/ [8 D
  36.     if($id > 0)
      _5 @" \: J) B. k% q, `. r) }
  37.     {
    , \) \5 {7 v- v& w. l
  38.         --$id; & F& `0 A, j% F' g# i
  39.         mysqli_query($conn ,'UPDATE ta SET id='.$id); / A+ f& e  b1 I( q2 y
  40.     }   z9 ?) v' I6 F. u- }7 b
  41.     # php的文件锁,释放锁
    $ m5 f. Q; V" `6 M2 {
  42.     flock($fp, LOCK_UN);
    1 n/ K7 i! M% r/ P
  43.     fclose($fp);
    * l; P( N6 I5 v& X) h
  44. ( `+ _, ?$ h) U( o( `: i
  45.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    7 a# I& `* D$ B
  46.     // while($row = mysqli_fetch_assoc($res))
    6 \* \0 K) }" k; y
  47.     // {
    ; Q; z6 y+ U+ t
  48.     //     $id = $row['id'];( D) @, ?+ A6 C1 `
  49.     // }
    5 H3 X0 U* a7 k) g" w; T  A; `6 X
  50.     // echo $id;
复制代码

1 S1 E$ a7 y7 y/ n
$ r0 a( p: t2 _" W
抢券活动实例:
  1. public function envelopeSnatching(){! W1 ^' y6 |# I9 d' B; W
  2.         $lingqu = $_POST['type'];$ d* B3 g( I6 k. P
  3.         $uid=session('u_id');//用户id8 Z# s5 w  o9 T
  4.         if(!$uid){
    ( Z# O. @1 q4 o$ N7 p$ m
  5.             $data['msg']='您没登录,请先登录!';
    $ p6 w' n, y* y3 v/ W/ S1 ^7 b
  6.         }else if(date('Y-m-d') != '2017-12-12'){! z3 |. [7 z4 P  T. L0 s" `
  7.             $data['msg']='不在活动时间内!';
    1 R4 L- [, Q" G: q, i; t% ]
  8.         }else{4 {$ q  S% D" X1 i& u1 s
  9.             $hours=date('H');//当前小时数
    - t* R5 v  I1 O5 ]" B% Y. G* V1 n
  10.             if($hours > '09' || $hours > '17'){+ u, O4 ~8 D+ o# B% {
  11. $ q* u* p# F, W# Y& [
  12.                 if($lingqu == 1 || $lingqu ==2){//点击10点的: _  W5 y) _# L; M: v0 E
  13.                     if($lingqu == 1){
    & G/ C1 l4 p8 [
  14.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过: s% k/ g* w2 b1 h7 F. N
  15.                         if($hours > '09'){; O- v' L* p" F2 Y  v2 O+ q
  16.                             $num=mt_rand(25,28);//优惠券金额
    + ^! g0 O& k! E. G1 D* J
  17.                             $id=1;
    % q9 j5 K+ f$ O! n
  18.                         }7 N6 a3 F2 _1 N
  19.                     }else if($lingqu == 2){9 H5 h3 W- u* ?$ ]8 Y0 T; d; t
  20.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
    8 @/ |- \( V( y& Y
  21.                         if($hours > '17'){
    # S- w4 E8 K9 s/ P( t
  22.                             $num=mt_rand(50,55);//优惠券金额
    ' w9 [) f: @' K! @& R, P) Z5 w
  23.                             $id=2;
    ( ]8 H$ p" b5 R- s) P8 |
  24.                         }
    / m) T9 l. ?2 I. u7 z
  25.                     }
    # o/ \$ T& X) c* m9 ]4 X
  26.                     if(!$id){6 |) a6 X: I( a, b/ H/ C
  27.                         $data['msg']='时间还没到,晚点再来吧。';
    ; m) f3 N+ n+ ~: @
  28.                     }else{) A) [/ H& b; @! d9 Z
  29.                         if($is_lingqu){
    3 |% h( J- ]' R6 f& j' E
  30.                             $data['msg']='你已经领取过了,留点给别人吧!';& b% o7 m4 |  O' f" M  ^. D
  31.                         }else{+ z/ q2 y  S/ S
  32.                             //锁表1 A8 b' Q4 j% y0 I1 s/ u1 @
  33.                             M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
    3 f4 o# T2 N1 y, ~" Z# A4 U1 V1 Q
  34.                             $active=M('active_num')->where(array('id'=>$id))->find();
    / ?7 d; B* d) y- ~. A
  35.                             if($active > 0){4 v# w& x5 H/ M7 o& F
  36.                                 //开启事务" O% d; n  a7 ?) r: B
  37.                                 M()->execute('start transaction');( N% @7 o, U4 x( h
  38.                                 $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
    : C; y( Z' Z4 C6 n
  39.                                 $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
    3 J( P4 o# o3 ]! F# S/ g
  40.                                 $members_preferential    =    M('members_preferential');0 Z. t$ p* a/ R
  41.                                 //对应投资金额,) F' [5 j+ [% b2 V6 l) V
  42.                                 $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));# Z% `, b! w6 {+ Z
  43.                                 $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');8 ?6 M; j; R4 ^" U" W% X# G
  44.                                 $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
    + o& P' N) P0 K4 m2 b2 @, }
  45.                                 $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券) e& f9 Z, |' ^  b2 m; G- v# Z
  46.                                 if($save && $add && $add2){' e1 u5 |- g4 ?8 M* f. m3 _
  47.                                     //事务提交) ]/ M1 x" e$ J8 u8 B  w2 o
  48.                                     M()->execute('commit');
    : L0 k' L6 J6 E! [+ u2 \# ^: \2 \
  49.                                     $data['msg']='恭喜你领取了'.$num.'元优惠券';7 `/ m' Z& R* a# ]' k
  50.                                 }else{
    : s/ h/ t( y6 R" A
  51.                                     //回滚
    ' K  N* h& c7 M/ a# M' [3 ~9 D
  52.                                     M()->execute('rollback');
    4 _9 d/ r3 J0 L
  53.                                     $data['msg']='未知错误!';+ v) W3 a7 k( y0 q4 v  f9 J. W
  54.                                 }
    ; B% {; T8 |+ l! w0 v6 Z6 o: P
  55.                             }else{3 L, x- D# }3 c8 X# a
  56.                                 $data['msg']='红包已领完,你来晚了!';. g9 Z( q. k. Y# i6 F. a
  57.                             }* \+ I, H4 ?# p8 P* Z' ^
  58.                             M()->execute('UNLOCK TABLES');3 D( n' ?8 f. B/ N7 ~; X7 G. \8 N# T
  59.                         }) ]9 _, d+ F1 K; U" {
  60.                     }% _4 T: `5 c1 M! ?% j. X
  61.                 }else{
    * W/ W6 b2 n3 _) t" P
  62.                     $data['msg']='非法操作!';
    , c/ R& G( u2 j) t' x
  63.                 }
    * T* d1 C6 g% p4 y3 k
  64.             }else{
    5 j( y- z+ R3 w7 r1 |0 L. n( H
  65.                 $data['msg']='还没有到活动时间,请晚点再来哟!!';  U( n( I$ N' {: q/ s* Z! E
  66.             }
    1 y  K6 f+ b' O$ |( o! V
  67.         }
    " c# T& D) A! U* C
  68.         exit(json_encode($data));, c4 Q8 q' F9 {0 c% i4 }4 l
  69.     }
复制代码

% _. X0 M. m! P0 W  W, c: Z; X& A! `: D% F" X! @9 m
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-6-19 22:38 , Processed in 0.052559 second(s), 20 queries .

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