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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

4 y% a) T: c  z+ b3 T+ V
Mysql中的锁语法:
+ _5 x9 i) _6 M3 a7 E5 zLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
4 I' O, }" W# n3 U/ TUNLOCK TABLES  【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
9 @# o/ P- d  N/ @4 x% sWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞" f5 a( H. x  i- f
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :
: g2 I9 H& X/ `文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
/ P2 V4 b3 @; ~2 R4 s' _9 D% `测试时,有个文件就行,叫什么名无所谓
总结:
" B1 V6 \  P$ H3 B项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:+ \9 a" _1 A1 }1 V! M# S# Z  z6 K
1. 高并发下单时,减库存量时要加锁/ _2 r3 O' H7 D' d  Z
2. 高并发抢单、抢票时要使用
Mysql锁示例:
  1.    /*, U+ }0 V. `  r
  2.     模拟秒杀活动-- 商品100件
    " v* z3 {3 @& O# y; V
  3.     CREATE TABLE ta, @1 ], O4 o1 Z( h) e
  4.     (! ?# R' G( F0 `+ J% f$ q9 l
  5.         id int comment '模拟100件活动商品的数量'
    3 B! u! _% O8 v' {! T: O
  6.     );! D/ Q8 f/ |4 d' J. C
  7.     INSERT INTO ta VALUES(100);) e- F3 [/ p' w' g: E
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件$ V7 {' j9 r5 P
  9.      */
    ) T, g2 W8 _. _/ `' u- G4 e( ^4 G4 X
  10.    
    / G1 U0 Z7 P( n  g% }
  11.     // 关闭错误报告
    5 S6 y; a/ m( a
  12.      error_reporting(0); $ Y, A8 n& N2 m8 [3 u: X

  13. # |2 _5 J' C  l& y4 Z3 Z
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址8 i& s' x/ K# F8 l. s$ B
  15.     $dbuser = 'root';            // mysql用户名% h8 c- E9 G9 F
  16.     $dbpass = 'root';          // mysql用户名密码
    / v# m5 n# w) ]$ s2 H" P
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);- U2 N& ?6 K8 |& q
  18.     if(! $conn )
    8 P- I+ ^: k6 V' j, o1 t7 \3 F+ m4 ?" r
  19.     {( |9 ?. Y) v+ Q8 N; @
  20.         die('连接失败: ' . mysqli_error($conn));
    , [) U5 M) U- u; C, s
  21.     }
    5 W; K" W, V& u+ S3 f2 q
  22.     // 设置编码,防止中文乱码
    1 A6 m3 M! v; O) C  f
  23.     mysqli_query($conn , "set names utf8");
    2 @0 O' V2 P! r5 S$ Z# X
  24.     mysqli_select_db( $conn, 'temp' );
    ! w& \/ m* Z/ d- x4 b: e$ L

  25. ( M- [% m/ T+ R7 x
  26.     # mysql 锁 7 }& f+ S) c! c$ t- w/ S
  27.     mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
    " R- |, X; a0 v* b
  28.     $rs = mysqli_query($conn , 'SELECT id FROM a');
    4 O0 r, ^- e. S) ^% \' {
  29.     $id = mysqli_result($rs, 0, 0); : n7 H( K5 @' f: s0 M- T' I* ^
  30.     if($id > 0)
    3 o% W. ^' S. `0 Y
  31.     { 8 X& q, _( W. c  N8 `
  32.         --$id; 1 ]$ z: O5 r$ ?0 V
  33.         mysqli_query($conn , 'UPDATE a SET id='.$id); / j5 P; k. k/ n/ y" y
  34.     } ! \! y) e* V3 C8 F0 U
  35. 4 Z/ k4 Y6 ^/ c7 ^  ], N8 I
  36.     # mysql 解锁
    * ^$ Q8 T! ^" H2 Y
  37.     mysqli_query($conn , 'UNLOCK TABLES');, v2 F  x% U1 ^. k% m
  38.     //查询解锁后的id值  v0 V+ L0 ?8 `0 s
  39.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    ; I; L7 ]# b1 g
  40.     // while($row = mysqli_fetch_assoc($res))
      B7 u  c7 t' z4 q8 z( h. \  W
  41.     // {+ M& N, C7 f( t
  42.     //     $id = $row['id'];- r( b# q2 ]( A! D
  43.     // }
    ) B: V3 ?; D) B0 s
  44.     // echo $id;
复制代码
5 F6 ]  f9 S) f

2 x5 k' w- X* _8 E/ u/ a

. [1 z; X/ a: q/ ?& @( n% S
PHP文件锁示例:
  1. /*4 {4 z! Q& |, T- \9 b
  2.     模拟秒杀活动-- 商品100件
    , m1 b, I6 i3 U* ]6 j
  3.     CREATE TABLE ta
    6 q  j/ s* o1 a9 e
  4.     (
    ' \7 [9 {5 K, V, X- l: N
  5.         id int comment '模拟100件活动商品的数量'" f5 @  D  Z% O) d( n% q4 S9 d
  6.     );  T% ]* [: F) x2 x) [  H* ^$ G
  7.     INSERT INTO ta VALUES(100);7 d) A- M1 U! I) A4 t& Q7 M
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件
    ' R  `- k4 M& a3 P2 w/ P3 K
  9.      */ $ \- L  H; u$ Z+ d$ A* x
  10.     + S, |# G) F0 n% _; D! A- Y
  11.     // 关闭错误报告
    ' h/ b" E. T$ k0 M, p
  12.      error_reporting(0); 8 B  ]. i% M+ Y# z
  13. * Z" X3 v$ U' }1 Y- D
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址
    7 G# z* f; N+ k, v( [
  15.     $dbuser = 'root';            // mysql用户名
    $ @: N0 [. h% J' Y- b% o
  16.     $dbpass = 'root';          // mysql用户名密码+ v% x/ h" f/ @4 t- }0 U
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);( q& y# n( o: m8 v7 p) X
  18.     if(! $conn )
    & h1 a7 Z. j; i0 w
  19.     {
    8 I1 e0 }* z5 v7 }+ @
  20.         die('连接失败: ' . mysqli_error($conn));) U) A$ h; I- S1 l+ v; }+ S. ]4 D
  21.     }4 v% H7 D5 G) Z1 c1 e, P
  22.     // 设置编码,防止中文乱码) e- D6 O& j/ C  m3 c7 K5 H
  23.     mysqli_query($conn , "set names utf8");
    * _' f9 S2 @( g0 P- n
  24.     mysqli_select_db( $conn, 'temp' ); & k/ G) y/ e% u; h% E$ I# k2 g# L, @3 e

  25. 5 v5 e7 U  B4 t2 Z
  26.     # php中的文件锁 7 P/ ?; ^! `, }$ b9 x' |# T& X; E) A
  27.     $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 7 G3 Z6 C& N$ S0 ?6 b" x# k; X
  28.     flock($fp, LOCK_EX);// 排他锁 * \' l: s3 P6 ]3 a' o

  29. 6 t4 S( ^& A+ W% @* ^
  30.     $retval = mysqli_query($conn ,'SELECT id FROM ta'); . D/ d0 j, s0 N  K0 @0 C) s
  31.     while($row = mysqli_fetch_assoc($retval))
    : `) R  z6 o, Q4 k3 E% P7 B2 {- v
  32.     {. r$ I& l3 L  ^9 L8 Y
  33.         $id =  $row['id'];$ n& Z) d" x8 G0 {6 W
  34.     }7 h7 u  f2 I( a
  35.     / e( J0 L4 r8 X' {/ C
  36.     if($id > 0) , r5 v# v1 f" J) y' |, t
  37.     {
    : ?% h. J% K3 q: u" P) ~
  38.         --$id; * G9 o4 ^4 a* H; W0 V1 r/ D
  39.         mysqli_query($conn ,'UPDATE ta SET id='.$id); 3 ~5 x' d. S$ w+ K5 ?0 Y1 ?* g
  40.     }
    . T9 \( K  A3 F# ~: ?* u( k
  41.     # php的文件锁,释放锁 * {. E2 u$ h9 s- u* U- X
  42.     flock($fp, LOCK_UN);
    - v& w/ ?3 G3 {. ~2 O' a! j
  43.     fclose($fp);
    . i% I# `5 Z0 F  }8 h/ Q; g
  44. 4 |, |$ E# N% e& v' U
  45.     // $res = mysqli_query($conn , 'SELECT id FROM ta');& e4 u, G7 Q# o7 J1 H1 i
  46.     // while($row = mysqli_fetch_assoc($res)): h) s! b8 X0 L# b
  47.     // {
    * g$ B* `; Q: U
  48.     //     $id = $row['id'];3 ?$ y! P9 a' T5 x5 `
  49.     // }
    7 o7 \4 h8 N9 `2 P
  50.     // echo $id;
复制代码
0 b* i* n, e' P! I4 k8 H

0 c# j) _1 M1 E+ M
抢券活动实例:
  1. public function envelopeSnatching(){$ ?- M) L' W' |, c' l$ {* X
  2.         $lingqu = $_POST['type'];) Q' f. Y4 h8 |; @& ^" d. R7 l1 e  d
  3.         $uid=session('u_id');//用户id
    " K* m" [" e4 K
  4.         if(!$uid){
    " K# s2 A4 r/ F. I8 J# E0 H9 S
  5.             $data['msg']='您没登录,请先登录!';5 c, q6 `! g, G/ M9 Z
  6.         }else if(date('Y-m-d') != '2017-12-12'){1 }$ \( }5 ~- G4 ^
  7.             $data['msg']='不在活动时间内!';
    ! M, }' G& P. r& C3 o7 V
  8.         }else{
    6 @. o7 j3 T. o
  9.             $hours=date('H');//当前小时数& o- `7 y  k1 p9 V4 q( T" @4 @
  10.             if($hours > '09' || $hours > '17'){( ~6 H) {; q! Y. ?

  11. " J3 _- ]0 r/ l8 N, Z
  12.                 if($lingqu == 1 || $lingqu ==2){//点击10点的
    5 Y+ g9 L0 g3 i9 W' T
  13.                     if($lingqu == 1){' m; c6 o) \# L4 J" z
  14.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
    6 t  R& M, }" i+ Y9 o! N
  15.                         if($hours > '09'){
    ' P' d7 J5 b4 {+ }, F% X# R
  16.                             $num=mt_rand(25,28);//优惠券金额
    . R* c9 t) c4 s6 f) O
  17.                             $id=1;# C  M# t& ^: e8 X0 O/ S4 C& U& s
  18.                         }
    % l, s% s* y2 H) u5 H$ K0 r9 }6 B
  19.                     }else if($lingqu == 2){
    ) G, j+ A5 A* h, n7 ?. R4 W8 v) c
  20.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();' q4 p- }6 q( i' q$ x- Z
  21.                         if($hours > '17'){9 ~; v' x, H% R/ {5 e" \# ^
  22.                             $num=mt_rand(50,55);//优惠券金额- ~6 K' R5 l1 u0 G/ x
  23.                             $id=2;
    8 f$ y2 N! C5 {
  24.                         }
    + o: \# t2 K% U0 C6 m
  25.                     }# T3 l* M5 Z$ P( K
  26.                     if(!$id){: \( @4 x9 |2 N) v
  27.                         $data['msg']='时间还没到,晚点再来吧。';! ]. w  e! Y1 Q5 [" r$ X3 Y# T) I
  28.                     }else{6 g7 U2 z2 L) T
  29.                         if($is_lingqu){. S8 q. ]( \9 M1 v$ a% ]
  30.                             $data['msg']='你已经领取过了,留点给别人吧!';4 v# e* F, U; B( ^! t
  31.                         }else{) [- ^* S( _3 [- ?6 c! W! X
  32.                             //锁表5 n# ]5 t/ ~  [% I% n
  33.                             M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');: v: [6 }% P" B" o; @
  34.                             $active=M('active_num')->where(array('id'=>$id))->find();
    ! s& H) K# s! ]% g
  35.                             if($active > 0){
    # r- O; X- x, t# W) u) \8 I
  36.                                 //开启事务
    7 t2 ?# m' ]9 F6 @8 j* d; J
  37.                                 M()->execute('start transaction');; @3 E# e; H9 G5 {9 I7 S
  38.                                 $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));6 w: F4 T# w+ P5 f- A$ _' H
  39.                                 $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
    $ ~; G4 Z# y2 l: z3 Z
  40.                                 $members_preferential    =    M('members_preferential');
    ) z, F6 _; B4 l6 d; i
  41.                                 //对应投资金额,
    / s* h" ~" T3 N" n5 Z
  42.                                 $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));4 ~: u: w6 Y3 s4 a7 W$ i
  43.                                 $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
    # G. _: b0 n! z
  44.                                 $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
    0 ~) C* C7 K1 U( P
  45.                                 $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券9 `/ A% Y) g# {* z( L
  46.                                 if($save && $add && $add2){0 ]0 z. z( ~1 m7 L# y, v$ ~4 U
  47.                                     //事务提交
    & V. b* Q/ u9 N( o
  48.                                     M()->execute('commit');
    7 c* Q8 x8 }& I1 i1 x3 Z
  49.                                     $data['msg']='恭喜你领取了'.$num.'元优惠券';
    . d; x' \! m! O
  50.                                 }else{
    & c9 h& m: \7 y: m  m
  51.                                     //回滚
    : h2 e$ A! H$ j4 b0 `: V7 A( g3 N" ^
  52.                                     M()->execute('rollback');$ |( ~& \; w' I4 e1 ?
  53.                                     $data['msg']='未知错误!';
    ) }, {4 j: v0 S/ h- i
  54.                                 }
    ' u1 T2 r, T/ ?: K9 j; g
  55.                             }else{! T  a' M8 |7 J8 \
  56.                                 $data['msg']='红包已领完,你来晚了!';3 @9 ?" N5 y' {- t6 Y: ]
  57.                             }
    4 z' c1 M7 p+ b! y3 f
  58.                             M()->execute('UNLOCK TABLES');
    4 P/ }6 T$ z2 s( V$ u5 J
  59.                         }
    % \2 B, C* i) z1 v9 ^3 `
  60.                     }+ j$ a7 v: a: \9 L
  61.                 }else{
    0 G& E( F% F( O7 I/ @: p8 z
  62.                     $data['msg']='非法操作!';
      n6 B3 ]9 X5 P0 D2 P4 {. G2 S4 [4 V
  63.                 }1 k/ x3 [, J- _2 P: H
  64.             }else{1 }- |! y7 n/ _" W: |
  65.                 $data['msg']='还没有到活动时间,请晚点再来哟!!';
    : J0 B2 x) e% o% T1 {+ _
  66.             }$ E4 `$ o" J0 _
  67.         }; B  ]* _2 H' O8 ?, D) J
  68.         exit(json_encode($data));6 K# g. O) s) \3 l+ O! ~
  69.     }
复制代码

( J& l, X! H- w4 M: c6 z  p
+ R2 i2 P& D0 \1 q& Q# ~
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-20 05:40 , Processed in 0.115139 second(s), 20 queries .

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