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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

$ H  D) n# W5 ~; E2 O2 f2 z2 F
Mysql中的锁语法:; r# z! H$ I$ i" F5 f( b
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
# T2 W. p, e$ ~: c' \7 L5 aUNLOCK TABLES  【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表/ A. {3 F2 f# H2 `
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞/ ^1 j6 y: R+ K5 z. z
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :
+ n" z2 r' t- q( d* Z5 u1 a" E文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
7 R) V+ e. J5 T4 R测试时,有个文件就行,叫什么名无所谓
总结:
' n% ~0 P* b, A项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:
* |6 ]7 Z9 {# ?- |( ]( X5 y; T; e1 s1. 高并发下单时,减库存量时要加锁4 U+ K; w/ R) o% |  T: ^7 v
2. 高并发抢单、抢票时要使用
Mysql锁示例:
  1.    /*, {8 @: @" j6 l# k& W# T
  2.     模拟秒杀活动-- 商品100件
    3 K- S( l8 |/ }' O
  3.     CREATE TABLE ta. H$ ?& A% R% s
  4.     (3 v& J" @2 p0 K, {$ b4 w2 W" j
  5.         id int comment '模拟100件活动商品的数量', ^, [! D6 f0 G0 \
  6.     );
    ) S6 l/ E# R" X3 F
  7.     INSERT INTO ta VALUES(100);) M' K4 r& `. I6 W" M" S, {
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件
    7 R/ l2 i$ w) m
  9.      */
    ; E, S& T2 c# k8 b. H" H
  10.    
    $ ]2 {+ W2 Z! A4 {, c, k
  11.     // 关闭错误报告) D; q, x0 D/ e/ y
  12.      error_reporting(0);   X: M: e; H" @2 [# v" P" d5 T

  13. 5 P! Y( M9 F% E( E- [6 T) E1 n9 K
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址
    5 t) E7 n0 i2 G% ]
  15.     $dbuser = 'root';            // mysql用户名
    7 S8 m# C2 T5 U2 D
  16.     $dbpass = 'root';          // mysql用户名密码% ~/ s0 l2 C* P0 @9 |
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
    3 a1 o. x2 m1 c3 Y+ o: }" k7 J
  18.     if(! $conn )
      e3 S8 D' r% S0 Z
  19.     {) g( I3 k7 v* G0 \8 ~# |
  20.         die('连接失败: ' . mysqli_error($conn));$ s0 ]8 W6 H% g
  21.     }2 r* a7 h: j- p
  22.     // 设置编码,防止中文乱码4 Y( F- [" e( ^
  23.     mysqli_query($conn , "set names utf8");# i$ N& `0 n" }& m+ Q+ r
  24.     mysqli_select_db( $conn, 'temp' ); : Y' F6 y3 W" k. K7 X, I# a

  25. & `& h1 v0 F  `1 d, k6 Y
  26.     # mysql 锁 1 l' y" o9 ^5 B$ z
  27.     mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
    " w  s3 f$ I6 w7 w0 e9 ]
  28.     $rs = mysqli_query($conn , 'SELECT id FROM a'); " ?8 i+ X/ M! J2 ?" n5 q
  29.     $id = mysqli_result($rs, 0, 0);
    8 G/ L) F! W$ k& k# Z8 Z
  30.     if($id > 0)
    7 Z/ R* T& a  k; r
  31.     { 7 b7 g4 j/ l8 ?! f2 Q" O
  32.         --$id;
    / l" E  u1 _9 B( X
  33.         mysqli_query($conn , 'UPDATE a SET id='.$id);
    8 L& f  s; L/ @8 }  g
  34.     }
    ) W/ p4 n7 B( g

  35. $ J! }- M; M* C2 e! c
  36.     # mysql 解锁 & D7 b( Q% Z( }: B
  37.     mysqli_query($conn , 'UNLOCK TABLES');+ f" \7 ]) X3 L0 H
  38.     //查询解锁后的id值
    7 C. f% m1 ]: d. w6 U3 }
  39.     // $res = mysqli_query($conn , 'SELECT id FROM ta');- |# H4 s) f" l4 _" U5 V6 V! o3 z( U
  40.     // while($row = mysqli_fetch_assoc($res)), z. F0 r) E) K, @7 J# p# a! _5 z
  41.     // {9 Z/ R4 _+ [! ]+ d
  42.     //     $id = $row['id'];
    ' F6 Z7 _  O" _: s, F. \) c
  43.     // }
    ( e: G( F% F+ Q4 H: v. O
  44.     // echo $id;
复制代码

7 K8 a  ?: m( R! K5 P1 g( E& ?  i: x8 b/ [+ S' i' ]

: W3 `+ l' d9 j( J8 G* _
PHP文件锁示例:
  1. /*3 l) F5 k* e( X+ a
  2.     模拟秒杀活动-- 商品100件4 m  m- J3 x1 Q) D! e' u3 l
  3.     CREATE TABLE ta% S: S7 l, }0 E, s
  4.     (, z, d8 n# y/ T& `! k3 c0 q
  5.         id int comment '模拟100件活动商品的数量'. }. z( C$ S. S
  6.     );
    ( h: P' F" e1 ?% ~7 G% C* n
  7.     INSERT INTO ta VALUES(100);
    3 U+ e/ f9 {- B  ^0 f
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件% S$ f8 U! o  O' ^9 H2 i
  9.      */
    # S& ~- t$ \9 e' `. }3 Z  p
  10.     ' k- ~! x1 k- H5 j/ r
  11.     // 关闭错误报告7 p# k5 B% N) W) r/ O
  12.      error_reporting(0);
    5 T$ x$ z- b; l, m/ f( b4 G  [

  13. 9 \- V0 ~" I7 D9 [* c
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址
    ; }4 l) e: p2 b. z( p
  15.     $dbuser = 'root';            // mysql用户名
    5 Y7 N1 V$ M% Z. [3 p. f# R
  16.     $dbpass = 'root';          // mysql用户名密码8 Z% d" U  k# N$ w
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
    9 {8 \# I6 Z2 }$ F5 i: o
  18.     if(! $conn )
    ( b! ~0 T) N' @' ]) c* A# m# S
  19.     {
    " V1 W1 }% o- a
  20.         die('连接失败: ' . mysqli_error($conn));
    1 G. Z' z& z" f2 z2 e) o' {
  21.     }
    * r/ V7 Y7 e2 p
  22.     // 设置编码,防止中文乱码: ]1 i% v) H# W- q: k( t/ b
  23.     mysqli_query($conn , "set names utf8");
    : Z" _+ N2 z* w3 X+ g9 [( O) \5 V
  24.     mysqli_select_db( $conn, 'temp' );
    , f" j- N9 A8 Y

  25. ' }9 k4 T3 s3 q8 n
  26.     # php中的文件锁
    9 _9 `/ e/ x; k# C2 \! p
  27.     $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 7 p  [! J/ P" i
  28.     flock($fp, LOCK_EX);// 排他锁
    ! |; b5 _# G+ f) I# x' e8 s1 Y: @* a

  29. 5 h! C+ e3 Q6 _* d% q
  30.     $retval = mysqli_query($conn ,'SELECT id FROM ta');
    . _! m( e: ^( N- Y; _: O6 y" b
  31.     while($row = mysqli_fetch_assoc($retval))
    2 I. N5 p9 P4 }2 Z" M3 f
  32.     {
    * R: z8 i4 F; S* I$ }
  33.         $id =  $row['id'];
    8 J, p( T3 O. i7 s$ \0 j2 D
  34.     }+ H  \" p# G& J& o
  35.    
    ( U* \/ j! l( A: f$ `+ l
  36.     if($id > 0)
    4 b6 R& R& b$ S; X0 \
  37.     { 4 r7 z* |' F- k/ b- B. Q8 Y
  38.         --$id; 0 W" f3 ~! E4 t
  39.         mysqli_query($conn ,'UPDATE ta SET id='.$id);
    ! r* R: M7 ~8 p& q+ e# m- |
  40.     } 4 y, K( m' H+ s; N( k- N: c
  41.     # php的文件锁,释放锁 & E* h' U2 M  z3 M1 E
  42.     flock($fp, LOCK_UN);
    * O) x$ t. g8 @, M# ]
  43.     fclose($fp);# P  e2 a# W0 ?  }
  44. ) Z" q+ K% ~; K
  45.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
      J/ n# W& m* h  ?
  46.     // while($row = mysqli_fetch_assoc($res)): ~: Z5 N7 u3 x* t
  47.     // {2 t, B! }$ N5 b% T5 i3 [
  48.     //     $id = $row['id'];; W1 u* n- p, M2 q+ i
  49.     // }" a  W5 C, x$ O& e+ N
  50.     // echo $id;
复制代码

1 g* q, L$ w8 H* p$ s% o: `2 e) [  S8 P& Q# Z
抢券活动实例:
  1. public function envelopeSnatching(){
    / Z5 u8 S& a1 w' F1 n
  2.         $lingqu = $_POST['type'];$ s0 Q, t8 q4 E: ]
  3.         $uid=session('u_id');//用户id
    4 S2 I) R" @. D: \  }5 q
  4.         if(!$uid){0 Z& `0 L9 a1 j7 O1 N: G2 O
  5.             $data['msg']='您没登录,请先登录!';4 d% l0 B0 W. n8 A# }$ b) k
  6.         }else if(date('Y-m-d') != '2017-12-12'){" b: H' |* ~8 C
  7.             $data['msg']='不在活动时间内!';# _* R4 }& k) T' V
  8.         }else{# Q% B+ d" Z" }+ z
  9.             $hours=date('H');//当前小时数; v4 h3 u: m/ A
  10.             if($hours > '09' || $hours > '17'){
    7 x( C  P7 Y0 x2 e1 s

  11. * D$ Q  e. F! O, A" W2 t" E( {
  12.                 if($lingqu == 1 || $lingqu ==2){//点击10点的# c+ L7 g0 b7 m% o* j
  13.                     if($lingqu == 1){
    # u: F( B4 [% C$ s5 Q. U! S
  14.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
    % {- E$ a2 z- Y( q
  15.                         if($hours > '09'){, U, E( \$ }5 R- p6 |
  16.                             $num=mt_rand(25,28);//优惠券金额+ X+ C/ V# Z$ @" }
  17.                             $id=1;
    9 h9 L; e$ R4 U* c
  18.                         }! k. q3 t& {6 |1 {5 H& O+ {
  19.                     }else if($lingqu == 2){( g; a5 B8 {! m7 M  g3 N7 ~
  20.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();2 x; P. \" q: G7 Z: H
  21.                         if($hours > '17'){
      `, x" q$ b6 R. l' w/ q
  22.                             $num=mt_rand(50,55);//优惠券金额4 O& Z2 Y+ u6 X$ s$ s! \
  23.                             $id=2;
    & M6 D- d$ z& ?5 @9 h
  24.                         }
    7 y, B( n% q* P
  25.                     }
    $ i2 _! a0 o2 L! Q* Y' y
  26.                     if(!$id){* _  M  K, l2 ?, v4 c* ?
  27.                         $data['msg']='时间还没到,晚点再来吧。';' l5 s+ I3 Y' o. e
  28.                     }else{
    + L$ }* i2 h. O5 ?! o
  29.                         if($is_lingqu){
    8 X- G( L7 S  q8 W4 K8 _
  30.                             $data['msg']='你已经领取过了,留点给别人吧!';
    1 I- d4 F5 q/ o( f& ~
  31.                         }else{( H* `# q2 q7 o; I
  32.                             //锁表4 J3 q' q  Q0 B; D: b
  33.                             M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
    9 B5 B5 R' j/ t$ Y1 ~5 H) b
  34.                             $active=M('active_num')->where(array('id'=>$id))->find();! n6 l/ n* O% U# n. X( v! ^
  35.                             if($active > 0){- w! C, p. n# l
  36.                                 //开启事务
    ' {% B" V% h* Q& M$ _) ^
  37.                                 M()->execute('start transaction');; E! W  N6 g7 s! Q9 `' u: R
  38.                                 $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
    - j" Z' f" p& `, p5 G$ e3 s" e+ D
  39.                                 $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));# U* ^0 g; o3 U1 r( _; ?
  40.                                 $members_preferential    =    M('members_preferential');
    . b  e* v/ R6 F) {! `. @
  41.                                 //对应投资金额,
    0 Y+ O+ s/ W& t: ?/ f
  42.                                 $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
    1 _) H' z! w* m, X: W8 G
  43.                                 $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
    7 W$ \$ [7 ^8 Y8 k: ^& P) u9 ?
  44.                                 $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
    % k" n1 r( s: {0 m* @- ]) e4 i# ]
  45.                                 $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券9 w+ @4 g, O& S# l. {$ t5 ?3 f1 d
  46.                                 if($save && $add && $add2){
    3 V9 V7 b$ g( E( P
  47.                                     //事务提交
    ) P2 m+ [7 W- a8 G. k# [! L
  48.                                     M()->execute('commit');0 Z4 U% B) z4 a* J
  49.                                     $data['msg']='恭喜你领取了'.$num.'元优惠券';
    " s2 L. L: c1 `% G
  50.                                 }else{! g1 Y6 b6 q6 H1 o; b% p0 E
  51.                                     //回滚' E1 O. B7 G5 E' {* K$ _6 T  m
  52.                                     M()->execute('rollback');- G" R9 y" n! y& m. T
  53.                                     $data['msg']='未知错误!';
    2 L$ o; s, H9 `
  54.                                 }
    - y& T! ?, a: q7 k$ M
  55.                             }else{
    ; L: O! t! P& L( K
  56.                                 $data['msg']='红包已领完,你来晚了!';4 t% @* h- k& A6 t) c, p6 u. a
  57.                             }
    9 c4 E- L  X8 A! \1 [
  58.                             M()->execute('UNLOCK TABLES');$ o, M9 B. k3 @' z- U9 i2 u
  59.                         }
    / s) ]9 I/ p: N7 J& [
  60.                     }
    6 L, ^. c5 u, _2 r6 h+ }* T
  61.                 }else{" O# C- J9 E( c8 w( v+ d
  62.                     $data['msg']='非法操作!';
    3 n' a0 M6 P; w  t) H" T
  63.                 }
    ( F( E; U( ~  U# g5 B( x
  64.             }else{
    7 w, Q2 Y, ]/ r% |
  65.                 $data['msg']='还没有到活动时间,请晚点再来哟!!';6 c" B* \5 C; ]7 N% j/ g
  66.             }; g  o0 `) F2 g' h9 P. S- g: r
  67.         }% A9 c8 C2 Y. [2 N3 ^
  68.         exit(json_encode($data));* F7 o1 s6 S7 w; @+ n7 g
  69.     }
复制代码

$ n& S4 d7 d6 R8 y$ t( `8 E& A6 l. r4 D  w
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2026-4-30 23:01 , Processed in 0.058332 second(s), 21 queries .

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