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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 4011|回复: 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 D6 n7 B- @) y1 N% |8 o) M
Mysql中的锁语法:
5 x; E4 ~! s9 n& W  v" R4 OLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
5 v9 p( X5 Z. N7 h' K& G0 |0 QUNLOCK TABLES  【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表- }2 F7 I+ ?9 e& V/ k4 S
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
; f+ V9 P6 u7 P& e9 X注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :
% [' [2 z  Z8 |/ h文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。* y% x2 E0 \  t* Q" E3 O5 p/ [
测试时,有个文件就行,叫什么名无所谓
总结:
* a, ^- k- Z7 L: R项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:
7 P" S5 U" J. s2 p! h! z1. 高并发下单时,减库存量时要加锁9 o% T& ]+ n' n% b  j
2. 高并发抢单、抢票时要使用
Mysql锁示例:
  1.    /*0 v. X# v: ?6 ~) D- B9 M
  2.     模拟秒杀活动-- 商品100件
    8 [" v- s  E/ I' [8 r
  3.     CREATE TABLE ta! }" q" F3 B& f+ g3 k! |" n! \
  4.     (
    : |' u& T0 w/ g* V4 ~) ^$ _! Z/ [
  5.         id int comment '模拟100件活动商品的数量'
    3 C: {. c- f: ]$ }, A
  6.     );
    ! H" R& c, ]$ U! x
  7.     INSERT INTO ta VALUES(100);
    + H! X5 F6 H  V$ f: ~9 W  E
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件. s! d5 V& h* @3 @# i
  9.      */
    & u' s5 t2 w; T- e6 O
  10.     3 i5 D* C/ t! V7 }/ ~% L8 D. g
  11.     // 关闭错误报告" t, W; C0 m1 z6 ]3 s* j
  12.      error_reporting(0); 7 p1 x0 X& _5 x) Q: q. Y4 b
  13. # u$ ?9 Y$ a1 l9 M3 V& \
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址
    # I$ }; _& U: i# W. `
  15.     $dbuser = 'root';            // mysql用户名2 X; c, W) g+ z
  16.     $dbpass = 'root';          // mysql用户名密码2 i- ?. R( F( }1 h* A
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
      G! p3 z9 r3 O2 a
  18.     if(! $conn )# R$ z+ ~. ]0 s6 G. w5 r8 V
  19.     {
    / _" b6 K" s" r  ]+ S+ A
  20.         die('连接失败: ' . mysqli_error($conn));
    & v$ L/ ~7 W( e
  21.     }9 G8 r7 Z) C2 L0 L# M  }
  22.     // 设置编码,防止中文乱码
    6 W3 w1 L% L) {& A3 N4 \
  23.     mysqli_query($conn , "set names utf8");" V: G5 {; R1 V5 F4 ]. J7 V7 m
  24.     mysqli_select_db( $conn, 'temp' ); 0 k) L5 Q8 o& c  N

  25. : `5 K# Q. d0 B9 C) L
  26.     # mysql 锁
    & A, g/ k+ ^# s' n; d
  27.     mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
    4 P. n+ i1 x' H" X1 F+ ]1 g$ a
  28.     $rs = mysqli_query($conn , 'SELECT id FROM a'); # I8 V8 S" I8 l
  29.     $id = mysqli_result($rs, 0, 0); ! W" M" R! q: X' X
  30.     if($id > 0) * ^( P. ~1 P5 p0 w2 D
  31.     { # |( M+ y+ R. u) a; Q( o
  32.         --$id;
    4 D% y. t6 F/ x
  33.         mysqli_query($conn , 'UPDATE a SET id='.$id); $ d% K+ \. {: N3 \4 g/ N9 O
  34.     } 7 E% P6 H+ Y6 m* {

  35. 7 u; u- b+ I1 j0 ]$ U$ f4 P5 u
  36.     # mysql 解锁
    2 R( m4 \6 n& W- p
  37.     mysqli_query($conn , 'UNLOCK TABLES');
    ; V! H/ K- Q0 p* }
  38.     //查询解锁后的id值
    - \) b& h3 S5 T6 ?7 {
  39.     // $res = mysqli_query($conn , 'SELECT id FROM ta');2 p& U, h+ j/ ], b5 `5 s
  40.     // while($row = mysqli_fetch_assoc($res))- Z/ x  V9 a7 p# y0 s) i
  41.     // {
    6 s, l" {' i1 @8 Z: b
  42.     //     $id = $row['id'];# J/ M' s" r$ X  c/ u
  43.     // }+ W9 x  o1 @. ?' S
  44.     // echo $id;
复制代码

# J4 [) ~5 c; s! {# L2 J  c3 ?
8 q( ~$ ]8 E' H5 V8 {) Z$ t' p
" H1 J5 K3 n; z% j  s
PHP文件锁示例:
  1. /*9 t! L) o8 y2 O5 I
  2.     模拟秒杀活动-- 商品100件+ G1 H( p0 |" G" n: }/ z4 t
  3.     CREATE TABLE ta
    ; f; Y$ _" ]/ `7 M4 f% }) d2 T
  4.     (1 {, Z0 L; `4 ?; l3 d
  5.         id int comment '模拟100件活动商品的数量': @& p5 v9 s& H
  6.     );
    3 q4 X* i: v! g* J# j# o
  7.     INSERT INTO ta VALUES(100);
    5 m8 L# P7 s( V4 ?) x; X' \
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件1 l* u3 @0 U4 n; h- \
  9.      */ 6 O9 o/ ~  k5 t$ K% p# ^( L0 a
  10.    
    ( G* ~1 P+ L& u$ L' b2 ^
  11.     // 关闭错误报告1 R* ~* d- u+ r$ {6 f+ g
  12.      error_reporting(0);
    6 C+ _; B/ }! V% \+ P

  13. 2 V# D, a" R( w$ G
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址6 c$ I2 |" m& K; u
  15.     $dbuser = 'root';            // mysql用户名4 y8 p: R1 Y- _7 s! h2 ~
  16.     $dbpass = 'root';          // mysql用户名密码- W* C. @# @4 S$ s! @+ W& o5 `; k
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
    & X/ b) m5 O8 I( m. n6 F/ p2 }
  18.     if(! $conn ); Q* W1 E8 `  f1 `0 P5 A
  19.     {
    $ H) e1 ^9 `. Z
  20.         die('连接失败: ' . mysqli_error($conn));( h, \* Z) U3 x& l& R' J/ H
  21.     }
    4 n6 B+ F8 }' ?1 Q
  22.     // 设置编码,防止中文乱码! D# l0 m0 E/ y; d
  23.     mysqli_query($conn , "set names utf8");- I6 I: a/ l1 z& Y# G$ [% r$ A2 Y
  24.     mysqli_select_db( $conn, 'temp' );
    / z7 A3 G9 Y9 y, L3 V

  25. ' {4 Y* b4 y) a9 X
  26.     # php中的文件锁
    1 c; U4 {0 c! V% [+ Z
  27.     $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 , u" `" S* g: ^$ {
  28.     flock($fp, LOCK_EX);// 排他锁
    7 Q; \. ^. j0 h2 t: A3 a
  29. $ b: b3 A% i- v! \
  30.     $retval = mysqli_query($conn ,'SELECT id FROM ta');
    + V8 }, X4 Q* ~7 k5 E% t2 W
  31.     while($row = mysqli_fetch_assoc($retval))
    $ v' d$ [, S. K5 K
  32.     {
    ! s0 P1 |$ D- ^
  33.         $id =  $row['id'];2 _. T% \. M, z/ }" g7 x: |! E/ w
  34.     }
    , W( Z$ r, P- y( Z# j
  35.    
    ( X7 \! _+ D8 C+ [7 X
  36.     if($id > 0) ) ?: J6 @6 A9 ?& v6 r: y
  37.     { ; a3 _7 w" [) ^3 N
  38.         --$id;
    1 I1 b; Z: |# r7 t0 Z0 A, ]& Y
  39.         mysqli_query($conn ,'UPDATE ta SET id='.$id);
    3 N: O, I# n: \! Q
  40.     }
    & {5 ^& c+ ~# v8 O( A) {; s( Y
  41.     # php的文件锁,释放锁 . X" z" r/ ]2 ^: j6 n2 h9 ^0 h# X; g
  42.     flock($fp, LOCK_UN); 2 ?" Z" j2 [; k& @+ X" a
  43.     fclose($fp);, Z6 i  D; U4 W0 z) M- b5 Y/ V, A8 j

  44. 7 N) C8 z$ ^, [; m
  45.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    6 `9 D/ K: F) d
  46.     // while($row = mysqli_fetch_assoc($res)); G0 s5 ^6 a& T5 V. O- ?" u. a8 U
  47.     // {
    0 E9 {1 e9 J/ C3 g# o) x4 K' x: F
  48.     //     $id = $row['id'];
    7 d7 u9 Q+ D" ~) U
  49.     // }6 X) D: ]7 ?2 J4 h
  50.     // echo $id;
复制代码
2 e& m& U/ y8 l
) p7 A& J9 o9 c2 {8 n1 V/ ?
抢券活动实例:
  1. public function envelopeSnatching(){# P  V. [8 j, y( D
  2.         $lingqu = $_POST['type'];0 q# y+ P3 B8 S$ M
  3.         $uid=session('u_id');//用户id
    2 A7 A! J* j! F. g$ ^
  4.         if(!$uid){# @# b/ f$ l& b+ `6 L5 ]
  5.             $data['msg']='您没登录,请先登录!';
    ( B/ v; v. K% ?; M: i1 _
  6.         }else if(date('Y-m-d') != '2017-12-12'){
    6 T9 M, {4 c: L1 @1 U
  7.             $data['msg']='不在活动时间内!';6 q0 R4 P: k5 O( R, M) r/ u
  8.         }else{. X. U) T5 S9 a9 E4 N
  9.             $hours=date('H');//当前小时数6 F9 U) Y- r* }/ b9 c: Y2 @
  10.             if($hours > '09' || $hours > '17'){
    5 w2 ?. c6 A6 e+ E

  11. / A( m( B. q+ d9 z$ P
  12.                 if($lingqu == 1 || $lingqu ==2){//点击10点的$ X5 x1 O: E0 e
  13.                     if($lingqu == 1){
    ( A8 p+ ^6 K! }
  14.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过0 i3 O5 d  R6 v$ Q
  15.                         if($hours > '09'){
    % G" |: \' u2 X1 H% L
  16.                             $num=mt_rand(25,28);//优惠券金额& X1 I8 a7 r* `
  17.                             $id=1;( w: U" S% m# W
  18.                         }
    * P6 |& Z7 ?4 _, V; K* }
  19.                     }else if($lingqu == 2){
    + S' ^1 x+ E6 B( }
  20.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();% K, q, c5 E0 E1 v
  21.                         if($hours > '17'){9 ^8 N0 {" y9 D; k+ S* Z" a
  22.                             $num=mt_rand(50,55);//优惠券金额
    2 _' R% l; V. z+ S. G" U8 N
  23.                             $id=2;
    + E9 Q# ~# u! W/ ^' S. f7 X
  24.                         }
    4 }! S7 }2 j' Q0 \6 t* T
  25.                     }) c$ |5 ^- ~6 G6 y8 x. r
  26.                     if(!$id){3 D" n% L: {8 {2 E( R
  27.                         $data['msg']='时间还没到,晚点再来吧。';% H- A( l2 u$ Y+ a, v* u9 h
  28.                     }else{7 ?( N- d, D/ d1 j& J. H+ |! @) |
  29.                         if($is_lingqu){
    * p7 t& a& b: e, k( p
  30.                             $data['msg']='你已经领取过了,留点给别人吧!';: X$ M! K3 b! G7 j5 t
  31.                         }else{
    ( o' W9 f2 i9 c) o) u3 [
  32.                             //锁表
    % G+ o/ c+ P. X; u! r4 S
  33.                             M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');7 F. h% X; n" j+ {
  34.                             $active=M('active_num')->where(array('id'=>$id))->find();9 x2 K9 C6 A$ X7 o, G, Z
  35.                             if($active > 0){0 b* w5 r4 _( ]) c4 m, G$ Y- a
  36.                                 //开启事务
    % {: }# H! m' B0 j; `. |/ X$ Z
  37.                                 M()->execute('start transaction');! _: D) z5 M5 K, |
  38.                                 $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
    " ^" F' \9 Q; K2 y% |4 l3 ^
  39.                                 $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));: w6 j2 `. r  P- K- m7 A1 G
  40.                                 $members_preferential    =    M('members_preferential');
    % i7 A( ?/ }7 D1 o. _& `
  41.                                 //对应投资金额,
    3 X- z' ]7 I! H, b- g
  42.                                 $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
      Z; Q  _- k3 K% `; r- P. q2 l. z
  43.                                 $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');% a1 `" X; M" v  ?  ^; S0 T  x
  44.                                 $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
    8 ~# w, X0 g, g0 H) z9 S$ A8 ]
  45.                                 $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
    ( q) j/ Z# s4 n; G: ?; y+ D
  46.                                 if($save && $add && $add2){5 _' w' G$ v" H2 E: f
  47.                                     //事务提交7 G( V5 A4 a) c
  48.                                     M()->execute('commit');4 v2 z+ L. p, P( l# v. F
  49.                                     $data['msg']='恭喜你领取了'.$num.'元优惠券';  e6 t( I) G) k4 m4 u$ o
  50.                                 }else{
    / j8 h6 I& |1 r. k+ X' F
  51.                                     //回滚
    " \* `! d. `+ F5 E8 Q
  52.                                     M()->execute('rollback');% ~  A! r1 Y3 o
  53.                                     $data['msg']='未知错误!';
    + k$ Y4 d! o0 N7 T/ F! i0 t+ ^! B
  54.                                 }
    6 @6 H, m5 E6 [( L, l2 f
  55.                             }else{
    $ v3 `5 W( R4 V4 E
  56.                                 $data['msg']='红包已领完,你来晚了!';
    $ K. y0 N9 T8 }. I' k0 x
  57.                             }& X: Q: E7 D- z1 z
  58.                             M()->execute('UNLOCK TABLES');
    / R; b7 ~* f3 o6 ~3 m
  59.                         }; u; @( t3 S1 `$ R
  60.                     }
    ) t+ x; }5 Q: T) k8 t3 I0 v  R# m0 s
  61.                 }else{
    : [3 k; Q- N, r% h
  62.                     $data['msg']='非法操作!';
      U; I: N: @: p
  63.                 }
    - B. \% f9 q: s/ F
  64.             }else{
    - p6 ]# W# [& u; O2 N
  65.                 $data['msg']='还没有到活动时间,请晚点再来哟!!';
    : H  A7 r) G. n& B0 h
  66.             }
    3 O* [/ _- o; g' Y+ i
  67.         }
    * X6 I* U3 G- j( I4 }- r
  68.         exit(json_encode($data));' O3 Z, P. z$ h
  69.     }
复制代码
1 g9 G& |; X# e& L
/ h8 F9 Y9 a* o; \  J
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-5-19 23:04 , Processed in 0.118900 second(s), 19 queries .

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