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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

5 }8 x: b) W. [$ m) h4 d) n0 K
Mysql中的锁语法:
! W: q2 {( o/ n2 qLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
6 {6 c6 f; e) y) HUNLOCK TABLES  【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
- E. h9 o4 C: A" hWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
& \0 z$ P7 c5 M+ t( p; z; O( d注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :: j& q. c) }, A+ T9 s$ @3 K
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
6 {6 s. X  H& m; i测试时,有个文件就行,叫什么名无所谓
总结:
$ z1 C4 Z. c, W+ C项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:
0 m4 F, D7 ]0 W6 B9 U9 |) \* W/ S1. 高并发下单时,减库存量时要加锁% [7 M0 ^# Q# p6 k% B
2. 高并发抢单、抢票时要使用
Mysql锁示例:
  1.    /*/ r, J% U( [& y
  2.     模拟秒杀活动-- 商品100件
    & F. C2 v0 L! Y! K$ d8 [5 `
  3.     CREATE TABLE ta
    2 B$ ?# |; A& `0 ~/ m2 l& @) ]
  4.     (  g1 x- P/ ]4 w- }9 F% S
  5.         id int comment '模拟100件活动商品的数量'7 ~9 h( ]6 ^9 Y! y9 W. r
  6.     );
    7 g2 A# a- t: V+ p- q
  7.     INSERT INTO ta VALUES(100);  m/ W, x, e* ^0 @$ i
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件
    ! n1 W2 V  `% i) w: C& J/ d8 `) [
  9.      */ * @* s# Q% {. F
  10.     % v$ o7 e3 F& O+ j5 a% i
  11.     // 关闭错误报告1 L$ G+ L5 }3 `; q7 R4 n
  12.      error_reporting(0); / U- ~# h8 R  i. ^3 z9 t
  13. : I% ^: h# R9 `  P
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址
    % z. y1 J+ F' V8 @% D: j
  15.     $dbuser = 'root';            // mysql用户名! F8 I& o& o8 h) M" L
  16.     $dbpass = 'root';          // mysql用户名密码
    ; M# A; W( k3 B/ J2 S; _
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);1 l, ~' o' y, t
  18.     if(! $conn )
    & l5 ?& e9 b" }& Y7 B: G
  19.     {) @8 \  e0 U* ^: ^7 d
  20.         die('连接失败: ' . mysqli_error($conn));
    9 A3 j. s- ?3 _
  21.     }
    ! p* }9 I$ d- W$ b1 g/ v
  22.     // 设置编码,防止中文乱码8 c2 u1 W. j+ C  L, M- L6 c/ k  o
  23.     mysqli_query($conn , "set names utf8");; h1 _9 d! Y1 J$ q
  24.     mysqli_select_db( $conn, 'temp' );
    , G: n3 u$ a- \& B

  25. & N- Z# H* a8 }# Z4 Y
  26.     # mysql 锁
    * K& d5 R3 S: w4 z" Y
  27.     mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
    ' U$ {7 s# ^* ?6 Q
  28.     $rs = mysqli_query($conn , 'SELECT id FROM a');
    ; p: d8 k2 p6 ^0 X0 O
  29.     $id = mysqli_result($rs, 0, 0); ( i3 k2 [5 A% Y+ v5 S8 \% c
  30.     if($id > 0)
    ( X% I6 ~% K( \9 V+ l# y: ]
  31.     { ' J8 c' n* [! X: P" E) `
  32.         --$id;   N- v9 V, d* Q. U+ F
  33.         mysqli_query($conn , 'UPDATE a SET id='.$id);
    % i6 Z' C* \) J/ y4 g5 T- H
  34.     }
    1 i4 v" c1 o4 V

  35. ! d0 C6 V# Z0 R- h
  36.     # mysql 解锁 . G- b' `" B0 t* E# n
  37.     mysqli_query($conn , 'UNLOCK TABLES');  g. }5 s8 F$ m# X8 [. ]# }
  38.     //查询解锁后的id值+ d* j4 I+ E6 t5 T& C+ W* M
  39.     // $res = mysqli_query($conn , 'SELECT id FROM ta');6 U4 v# Z0 j( T# ~
  40.     // while($row = mysqli_fetch_assoc($res))# l* q1 X$ e1 [6 e
  41.     // {: ]3 L( o7 ^# e$ ]
  42.     //     $id = $row['id'];1 `# A* a2 i/ F. u9 t6 \: S
  43.     // }* Z' c( E# {$ Z, y+ B! R/ S
  44.     // echo $id;
复制代码

  g5 ^/ g6 `) W% @" }, I0 c: Z7 U  x0 C/ z
! s+ b# U4 v- o& Y) W
PHP文件锁示例:
  1. /*
    ' n. K/ S9 ~. y! [4 n: p
  2.     模拟秒杀活动-- 商品100件
    4 X' J! x2 p' N8 ?; c0 j
  3.     CREATE TABLE ta5 M4 T3 \4 _, \0 m' U& c/ v) T
  4.     (
    " b$ O/ r) @: {& o
  5.         id int comment '模拟100件活动商品的数量'6 g+ ?" r4 T, w) _: I  C; U) [0 T
  6.     );( G3 C  S5 X. G2 S3 P
  7.     INSERT INTO ta VALUES(100);
    . \/ x/ k! X' p+ o& X- n  B
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件
    3 u$ [$ ^( ]& f, j% h
  9.      */
      b0 |, u8 a6 A( `- g
  10.    
    6 y( u! Z8 B: r
  11.     // 关闭错误报告8 {! x) }9 y$ H& u3 `
  12.      error_reporting(0);
    / W# W" w+ X0 _! v* g
  13. 3 B! \* X4 x# u8 F' U- L# a
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址( N! @* e% W. E7 k, C0 v( b
  15.     $dbuser = 'root';            // mysql用户名3 M0 I" E- P7 W8 A
  16.     $dbpass = 'root';          // mysql用户名密码
    - a- |( Y/ \% o# t. }( k* B
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);& }* {) M: W8 e
  18.     if(! $conn )
    + Y0 N/ E- H/ ~
  19.     {
    9 v# |' \, I2 @/ o
  20.         die('连接失败: ' . mysqli_error($conn));
    5 B( ?+ ?; z( t  d- }
  21.     }
    . n, E, N, e& T# ]
  22.     // 设置编码,防止中文乱码. |' L3 j; k5 u' {+ A
  23.     mysqli_query($conn , "set names utf8");* M) a$ ^! J: E$ P; v: H
  24.     mysqli_select_db( $conn, 'temp' );
    : a% _3 O, ^0 l% Q
  25. 8 X1 l3 [: }8 k# C8 d
  26.     # php中的文件锁
    1 d* t6 ]' W# ^' D: ~) ?  i) H+ [
  27.     $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 * l$ j2 W' K& G
  28.     flock($fp, LOCK_EX);// 排他锁 ( F/ J6 G9 i! O. H8 h
  29. - N1 L9 x! t( I7 h
  30.     $retval = mysqli_query($conn ,'SELECT id FROM ta'); 2 r: ~) a3 E/ u7 E# Q
  31.     while($row = mysqli_fetch_assoc($retval))
    ! z, }) X+ t' m
  32.     {& s! b: z% r& i
  33.         $id =  $row['id'];
    7 s5 i  m5 y5 Q
  34.     }, J5 p; Q1 q( O; W' h0 b1 @5 v1 M
  35.     + h5 S- v, O9 l  d
  36.     if($id > 0) # A  C* N. V9 {9 |: l
  37.     {
    / F7 R6 k6 u- ?  u3 I6 U
  38.         --$id;
    1 B/ x% g8 u( Y( h
  39.         mysqli_query($conn ,'UPDATE ta SET id='.$id);
    / X2 m: W( W6 v- x+ F/ w
  40.     } $ F/ y4 V0 K1 N. l7 ]% Q9 D- Z
  41.     # php的文件锁,释放锁
    5 m- P# a7 a/ Y5 e2 T/ j6 |
  42.     flock($fp, LOCK_UN); 5 j! {/ @# R1 B3 [' v0 A
  43.     fclose($fp);* x0 j6 F; ]% ~& T

  44. . x7 b$ @! n! \- W
  45.     // $res = mysqli_query($conn , 'SELECT id FROM ta');( A' Q, e# q$ B/ U& m/ q
  46.     // while($row = mysqli_fetch_assoc($res))
    % ^0 m3 X5 b! P. Y2 R/ L9 w
  47.     // {6 X6 E$ R, |2 R
  48.     //     $id = $row['id'];
    5 {- L9 i9 K1 K/ ?+ d7 O8 W1 k/ j
  49.     // }
    . @- R& c' b% Z& F  c$ B
  50.     // echo $id;
复制代码

* A, \1 J3 E& _6 r9 S$ Y+ S1 `- Y8 R+ |  z7 T5 F4 Y
抢券活动实例:
  1. public function envelopeSnatching(){' B# B* M8 `0 X. I+ H3 T
  2.         $lingqu = $_POST['type'];0 r& v- \2 z' Z, K, j
  3.         $uid=session('u_id');//用户id
    8 ?: ~, c) a. X; X( u- F9 _4 i
  4.         if(!$uid){
    - J5 K# y- Z0 x" |, p
  5.             $data['msg']='您没登录,请先登录!';( x4 K0 g* @5 ~9 t
  6.         }else if(date('Y-m-d') != '2017-12-12'){
    ( W4 H7 |' j6 `+ d2 P3 c
  7.             $data['msg']='不在活动时间内!';
    $ Y8 _! _6 M' [& V* D1 e7 I
  8.         }else{4 D" F  u$ }1 m7 A* _8 e7 R
  9.             $hours=date('H');//当前小时数/ l8 i' }1 k1 ?" C' G
  10.             if($hours > '09' || $hours > '17'){
    ) k. H: e1 O: d' t0 S

  11. + m  U& y+ h, R3 |6 W
  12.                 if($lingqu == 1 || $lingqu ==2){//点击10点的5 h+ K6 Z0 R& S3 ~% R# n  ^
  13.                     if($lingqu == 1){' y8 @1 k% I+ D
  14.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过  ^7 F! H. [2 M' E6 y/ u/ J
  15.                         if($hours > '09'){) I7 Q+ @$ J$ G% k$ i/ N1 W6 M
  16.                             $num=mt_rand(25,28);//优惠券金额
    : u7 f7 K- j9 _  W  Z
  17.                             $id=1;2 o0 L4 m5 o" i
  18.                         }; a: g8 \4 p( b% Z+ C8 _4 ?
  19.                     }else if($lingqu == 2){& y( U* M3 [% {6 K
  20.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();; V% D. J6 `. E9 x) a5 l) ^+ @
  21.                         if($hours > '17'){
    7 T# Q. Q7 M* @2 F
  22.                             $num=mt_rand(50,55);//优惠券金额
    ; L3 d' Q- ~$ n
  23.                             $id=2;% `  S) I0 z- C- n
  24.                         }# L( o3 J' O- M+ z1 O( n
  25.                     }8 b  g1 p  Z* q% a
  26.                     if(!$id){
    ) y; k" S( V* M3 G9 O
  27.                         $data['msg']='时间还没到,晚点再来吧。';" f5 p  i8 {& H+ M1 m
  28.                     }else{
    : O2 v/ c% ~- P$ p& _4 Q4 W6 d) Q& l
  29.                         if($is_lingqu){' `4 k2 x) I- v' E( Z( G5 r5 W4 q5 Y
  30.                             $data['msg']='你已经领取过了,留点给别人吧!';
    . N0 E  A  F3 |% [; C  l
  31.                         }else{
    7 \- z! b# z2 s6 s, Y
  32.                             //锁表
    7 C/ k3 B% D- w- Y7 s! y
  33.                             M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');0 D/ D! A2 E1 @. W4 t" r. }  Z  Q) U
  34.                             $active=M('active_num')->where(array('id'=>$id))->find();
    3 |; }* z% I' `  S. u7 J( h" a
  35.                             if($active > 0){4 ?; z* s3 N% M* U! `1 h4 m  R
  36.                                 //开启事务
    # }) r, }- z) J8 _# C6 v
  37.                                 M()->execute('start transaction');. N2 {1 i9 r2 Y. M! n& k
  38.                                 $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
    # S9 Q7 i8 Y/ g. t
  39.                                 $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
    1 l; v3 ~; |+ H1 Y( G/ c
  40.                                 $members_preferential    =    M('members_preferential');  f. H5 |. t8 o& i5 v0 t& U$ P
  41.                                 //对应投资金额,
    7 o3 L( s+ w% P( a9 v
  42.                                 $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));  I- K/ C5 v* J8 h5 A* _; K- o
  43.                                 $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
    & X4 l$ M  e: B- k; G
  44.                                 $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间# v, e* p" K1 P4 u! b
  45.                                 $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券$ R9 x0 @  g4 s2 X7 k# x4 L
  46.                                 if($save && $add && $add2){: V7 U6 c" O1 _  g
  47.                                     //事务提交
    3 V5 B: p9 X. u1 t9 ?$ I. w# u
  48.                                     M()->execute('commit');9 V) B/ c& e# G4 q* V
  49.                                     $data['msg']='恭喜你领取了'.$num.'元优惠券';' i5 b  B5 M, E9 N% K  b
  50.                                 }else{
    8 h* n# d0 l9 S+ e
  51.                                     //回滚% G3 q; b1 L) e  q7 i
  52.                                     M()->execute('rollback');
    " J9 m( [# ]/ C: Z$ z+ J
  53.                                     $data['msg']='未知错误!';
    + A7 N/ i  Z1 {# Y% q8 Z
  54.                                 }
    $ [* o0 a; L7 {) s0 e7 z* }/ Q
  55.                             }else{
    ) T1 h3 _, r: j2 [" r* q7 B
  56.                                 $data['msg']='红包已领完,你来晚了!';
    ) }, s2 s) l: j( F. i, |
  57.                             }
    ! ~5 g: Y4 p( C8 |7 A
  58.                             M()->execute('UNLOCK TABLES');) N- t5 A/ N( I; T
  59.                         }
    : f9 E4 l+ u* R5 ]
  60.                     }) M  z  }  R0 F$ v: k# `
  61.                 }else{# N: @+ ]3 g- l# \
  62.                     $data['msg']='非法操作!';' [/ D) ], @, N# F: W
  63.                 }+ O1 k* B5 M& O* m1 d
  64.             }else{
    . K: W$ }1 M" B8 E- Z& I
  65.                 $data['msg']='还没有到活动时间,请晚点再来哟!!';) N* O- ^. z0 [! f. V
  66.             }
    7 j: i% h9 R+ v* Z- K- `6 a( a/ Z
  67.         }' y6 v4 x5 S) |
  68.         exit(json_encode($data));
    ( h. }8 k$ E$ y) H$ {- Q$ A
  69.     }
复制代码

/ g- c* {7 D* |  r' z- F. g# e7 T' k% B2 S, o
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-6-20 03:10 , Processed in 0.055478 second(s), 20 queries .

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