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

QQ登录

只需一步,快速开始

 找回密码
 立即注册

QQ登录

只需一步,快速开始

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

  j8 I: Q, a  `& R: Z( x& C
Mysql中的锁语法:
6 ^( B( ?/ ?" L/ E6 ULOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】8 c# m& h4 z0 }
UNLOCK TABLES  【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
5 D* Z. M% m# qWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞9 I, x- D) u' }, K  e% A
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :8 s! L# J* u+ I0 F
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。6 v! f- q3 u9 D& k
测试时,有个文件就行,叫什么名无所谓
总结:
/ t9 h) m: M8 h+ a4 h/ R项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:% R& n9 R) ]8 r9 F) c" ^
1. 高并发下单时,减库存量时要加锁, K! Z% K) {, L
2. 高并发抢单、抢票时要使用
Mysql锁示例:
  1.    /*; e5 w  s# _9 i, i; p+ H( u
  2.     模拟秒杀活动-- 商品100件8 f+ R* d% |6 q" k' x9 X& j% Y
  3.     CREATE TABLE ta' ^& a$ o+ }! }1 N& a: j& c
  4.     (
    ' q' S# o: m4 O, u5 a: E) v  i
  5.         id int comment '模拟100件活动商品的数量'8 j9 T8 K. N9 ^; n) q5 E! V! S& t
  6.     );1 |* T: x+ Z& f4 o  C" H7 h
  7.     INSERT INTO ta VALUES(100);, M, o0 N" q! V3 |! g- z
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件4 W, g5 A  Y; M  P& A0 |
  9.      */ : |" |: R; c( J& s- `
  10.    
    : o7 S) s, ^; N6 b9 B# c# {
  11.     // 关闭错误报告: M9 p5 W" j- Z" u: o
  12.      error_reporting(0); - x3 P1 [  h, a; ^/ d: c! J
  13. ! S( |' {. t; {; E5 ^# W3 q4 w$ @
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址
    9 b7 B& ~  q) `
  15.     $dbuser = 'root';            // mysql用户名. o/ r0 ^2 p% q0 u
  16.     $dbpass = 'root';          // mysql用户名密码
    4 z( c5 ?" d" ]" m7 `7 R
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);- p) {. G( G0 W  |3 ~
  18.     if(! $conn )
    4 G$ I7 A& K$ U7 h
  19.     {0 N# L9 ^$ X5 E# x
  20.         die('连接失败: ' . mysqli_error($conn));* {& `1 g' `8 d; l/ \- B
  21.     }7 ~# _9 a) z, }! R# ]7 Z
  22.     // 设置编码,防止中文乱码; d) z" l$ d" ]  R4 l0 n( g
  23.     mysqli_query($conn , "set names utf8");6 Q% x# u3 Q3 J# ?! s- d* `  T/ x
  24.     mysqli_select_db( $conn, 'temp' ); # N4 q' s* \6 [8 q% a
  25. 5 y! W9 Z+ q& X! u3 ~% i& t1 S
  26.     # mysql 锁
    , q+ C1 h$ c3 m4 |0 Z7 ~
  27.     mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
    7 }1 B7 i5 z- d7 A: @
  28.     $rs = mysqli_query($conn , 'SELECT id FROM a'); ( P5 R! D% ], O  \7 D$ M
  29.     $id = mysqli_result($rs, 0, 0);
    7 }- s. M. {  U$ M- k
  30.     if($id > 0)
    ! i$ [- h5 o/ F5 S7 D2 o- i. `
  31.     {
    # f4 X; r6 J5 s) j- q: X3 U3 @" y
  32.         --$id;
    ( |$ @7 t/ w! }
  33.         mysqli_query($conn , 'UPDATE a SET id='.$id); : G  R5 Q; x5 h: `: u& e
  34.     }
    " @* c2 i, R* n" @; v
  35. 8 X* D# j2 Q* H  D( T$ R! B+ a! f
  36.     # mysql 解锁
    8 p  g: E' w( H; _5 I5 e
  37.     mysqli_query($conn , 'UNLOCK TABLES');6 Z9 x  |6 B2 c) q
  38.     //查询解锁后的id值
    % U0 k, i& \( Z. {+ K: j
  39.     // $res = mysqli_query($conn , 'SELECT id FROM ta');
    " f) O2 S5 T( \0 l  J: o( y4 G
  40.     // while($row = mysqli_fetch_assoc($res))* j4 O9 N0 x7 n. ^+ i- k- i
  41.     // {
    * t+ }; }8 ^+ U7 Q! X8 _+ L3 m
  42.     //     $id = $row['id'];6 U* i5 V* N  r  N: o+ e
  43.     // }2 a& E  B6 \1 @
  44.     // echo $id;
复制代码
, X$ n4 D) R6 s9 V; I
0 P- [" X4 [+ N  m
/ p. O1 l/ T/ I; l/ @
PHP文件锁示例:
  1. /*
    8 C. f' k1 u1 x) S* K& K" ]4 h
  2.     模拟秒杀活动-- 商品100件
    ' T; z, f7 w% Z6 _' E2 ]- ]: X. }
  3.     CREATE TABLE ta$ b- C6 g& Q" A
  4.     (
    * C& T3 t* G6 Z4 U0 b0 A
  5.         id int comment '模拟100件活动商品的数量'
    5 n9 F% F8 Y8 ?
  6.     );8 d1 d  |1 r' A) _* C
  7.     INSERT INTO ta VALUES(100);. J- `3 K) V; c5 R; I' e, n/ Z# J% G
  8.     模仿:以10的并发量访问这个脚本!    使用apache自带的ab.exe软件
    / L# c  E+ J# @' R
  9.      */
    . D/ u! U1 v4 S( p
  10.    
    ' y( O1 X6 y  f6 O; z
  11.     // 关闭错误报告
    1 `- @) e8 ?( d% r4 h2 j0 r
  12.      error_reporting(0);   w) L5 x+ I% L' L2 ]

  13. 9 \( q1 f7 O  m. i% C
  14.      $dbhost = 'localhost:3306';  // mysql服务器主机地址
    * v1 T1 M2 j, u4 c
  15.     $dbuser = 'root';            // mysql用户名9 _! b5 ~1 a1 y7 n4 O
  16.     $dbpass = 'root';          // mysql用户名密码" N% B, r  m1 C
  17.     $conn = mysqli_connect($dbhost, $dbuser, $dbpass);& U. p1 Q/ k0 P
  18.     if(! $conn )2 \8 b' o* E' Z8 s; X* K: E' o
  19.     {
    ; p7 g3 |' p# X6 y1 _/ p
  20.         die('连接失败: ' . mysqli_error($conn));
    8 Y5 f: j' w# {) g1 |  B
  21.     }
    1 D0 _' J& V! {  P7 s
  22.     // 设置编码,防止中文乱码( e6 u" s6 J1 d
  23.     mysqli_query($conn , "set names utf8");# C2 {# ~6 Y' W% S
  24.     mysqli_select_db( $conn, 'temp' ); $ K0 D' m7 c* q
  25. * I3 X- d8 n" g1 U6 n
  26.     # php中的文件锁 ) W+ d9 H! m. a4 S2 ]- N
  27.     $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
      M' w. j. D2 C/ }& |
  28.     flock($fp, LOCK_EX);// 排他锁
    % G% ]- ]/ E' Z, Q2 }
  29. , Z* |9 q7 c4 a6 P4 Y
  30.     $retval = mysqli_query($conn ,'SELECT id FROM ta'); % i1 c2 b& I+ E# h8 c
  31.     while($row = mysqli_fetch_assoc($retval))8 R! o+ ]; i# i
  32.     {+ H1 W. j' {6 X0 K$ W
  33.         $id =  $row['id'];, g6 k! e6 _6 x/ ]5 r
  34.     }% q# Q) W2 @4 O  g
  35.     5 F- n3 X& F; }$ |; M, K
  36.     if($id > 0)
    * \2 e& r0 Q7 y; B" Z
  37.     { $ I' z' m  T( [
  38.         --$id;
    ; T: |/ a) \  U! Z
  39.         mysqli_query($conn ,'UPDATE ta SET id='.$id);
    * I: q# K7 c, w. ~( s
  40.     } / F9 c$ n7 b/ k5 S; u
  41.     # php的文件锁,释放锁
    0 t- e' c# G+ @# ~& w# ~" H
  42.     flock($fp, LOCK_UN); ) w8 ^( X6 V: i  x
  43.     fclose($fp);
    5 ^: ]" G6 v% j5 i& F
  44. * l2 U5 _9 J- Z5 _! ^$ f
  45.     // $res = mysqli_query($conn , 'SELECT id FROM ta');2 ^3 C5 t9 g9 g. [
  46.     // while($row = mysqli_fetch_assoc($res))
    " `" L9 V2 Y* x
  47.     // {2 X! S! [, a; ?/ i, Y
  48.     //     $id = $row['id'];5 q, P  @/ A5 X, V7 d
  49.     // }/ O+ w5 Q/ g$ G' T% W& c
  50.     // echo $id;
复制代码
% M) l& v9 y- C8 H& |- Y; S

0 j2 \; ^7 T& }" |" e$ \9 V1 r3 E
抢券活动实例:
  1. public function envelopeSnatching(){$ C! Y: y: w8 O3 _( S- ?& h7 L
  2.         $lingqu = $_POST['type'];7 \0 z) D6 f& ~+ l3 C$ @
  3.         $uid=session('u_id');//用户id
    + j/ r# {$ }# ~* a( I
  4.         if(!$uid){
    2 [: d3 [$ v3 I8 c
  5.             $data['msg']='您没登录,请先登录!';) i1 _  |* @# R
  6.         }else if(date('Y-m-d') != '2017-12-12'){
    . N1 a* r, T, n* ?% ]9 l( l& @
  7.             $data['msg']='不在活动时间内!';* P( L7 q& F: c
  8.         }else{; D" z: H% q8 \: K# ^
  9.             $hours=date('H');//当前小时数7 i+ R- T3 [6 ?7 a' J
  10.             if($hours > '09' || $hours > '17'){; }+ y0 c1 G) z# V$ k7 \& g$ s

  11. - n/ D9 o# E6 J; Y3 G# o
  12.                 if($lingqu == 1 || $lingqu ==2){//点击10点的
    ) G4 V- O, y0 W$ P$ z" N# M
  13.                     if($lingqu == 1){' j6 h! E3 f& z+ Q3 A, q/ {9 q  i
  14.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过: v+ I( Y# N' P$ @5 Z
  15.                         if($hours > '09'){+ W4 G  C- V1 t8 {5 P. i7 ?
  16.                             $num=mt_rand(25,28);//优惠券金额/ f6 \+ I  f  H) S9 o* Y1 o$ h
  17.                             $id=1;
    ! n8 c# ]0 u$ x4 C/ r6 }
  18.                         }
    ; z; ]5 a8 _7 A! x; P4 Q
  19.                     }else if($lingqu == 2){  s3 j4 T! J0 p# h
  20.                         $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();! z. w9 z" n- O0 C
  21.                         if($hours > '17'){& ^; T* V* g0 w, P
  22.                             $num=mt_rand(50,55);//优惠券金额
    0 l, F) ~% `- v1 ~1 E
  23.                             $id=2;
    4 S& W/ h. X3 |$ H9 O* g& G9 y
  24.                         }4 F# i9 w1 {, C9 ]9 Y& R
  25.                     }
    " a3 ~+ A# D/ \  [. S
  26.                     if(!$id){
    9 B3 w. k" [5 J. S
  27.                         $data['msg']='时间还没到,晚点再来吧。';( T! i9 P& b0 ~# K4 v
  28.                     }else{
    ) J8 _( B/ c9 h) D  W6 J
  29.                         if($is_lingqu){1 z( Y$ v# e# r
  30.                             $data['msg']='你已经领取过了,留点给别人吧!';
    , j, u7 r: N! M! x0 k
  31.                         }else{
    6 k  |4 d5 \; B5 V0 H
  32.                             //锁表
      I4 c/ u$ k- _( m8 f$ b$ l
  33.                             M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
    5 g# E; ~: i$ A6 W/ I" F$ O! U% [
  34.                             $active=M('active_num')->where(array('id'=>$id))->find();2 i) h* c& K. C( T4 i/ m
  35.                             if($active > 0){
    % s# P0 ~$ W  p# N  a6 a/ C
  36.                                 //开启事务
    : z! }3 z- g/ ]4 \- X) r/ S
  37.                                 M()->execute('start transaction');3 m, r! r: {. d- q, G% ^. j; l
  38.                                 $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));: d1 Q: f$ j$ i- J! X
  39.                                 $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
    ) L. _; }+ r* |: `5 }
  40.                                 $members_preferential    =    M('members_preferential');
    ( U! K5 V% T1 q3 y& v- p
  41.                                 //对应投资金额,7 J9 n" k5 y" H9 R! R* j
  42.                                 $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));7 S4 U) G$ V( J- L. D, o1 X
  43.                                 $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
    , t4 L: f/ E6 C: p" U* J
  44.                                 $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间, I* o1 |2 u9 ?6 U# w9 l" k% C6 Z
  45.                                 $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券2 u5 [# j* a9 N
  46.                                 if($save && $add && $add2){
    * ^. @$ ~# ]5 Q, k
  47.                                     //事务提交
    + m/ i+ V& m; {- o: |
  48.                                     M()->execute('commit');" R% T  C, ~( G7 M- q  Y5 l
  49.                                     $data['msg']='恭喜你领取了'.$num.'元优惠券';3 V, G$ r% V8 L* q
  50.                                 }else{
    # }( q6 K" g: I' `& P
  51.                                     //回滚
    / V8 e8 [& S. }# }( T
  52.                                     M()->execute('rollback');8 t2 d8 g/ e  ?% o8 T* U$ f
  53.                                     $data['msg']='未知错误!';9 l/ M. ~( e+ [+ U) M
  54.                                 }
    7 `+ u" y( _, J( C0 X% r
  55.                             }else{/ ?9 Q# v+ M0 B4 q5 c3 \
  56.                                 $data['msg']='红包已领完,你来晚了!';
    ) Y# H% U: A, z6 K! A* ?
  57.                             }
    - N  g6 G2 b/ r4 k  ^; Z
  58.                             M()->execute('UNLOCK TABLES');
    3 m* ]# V2 J, d6 g
  59.                         }
    & A( a5 T' I' I- g, V# f; J7 n# W3 Z
  60.                     }5 d& w1 h& A- L9 v  @# Z) }
  61.                 }else{
    0 B! x# U9 s9 [2 C( F2 U, V
  62.                     $data['msg']='非法操作!';1 c# q' Q' Z7 T
  63.                 }! H; W4 M6 m, L! ]& F* j' s
  64.             }else{
    0 ?# X8 G7 g9 k! O, l
  65.                 $data['msg']='还没有到活动时间,请晚点再来哟!!';0 n5 h" T1 Y6 n# T2 q+ ]
  66.             }4 U9 k' i* \  I! F  t
  67.         }
    / r9 q6 k9 c! J! ?0 J+ }
  68.         exit(json_encode($data));
      F9 M  |- x+ |, [4 R
  69.     }
复制代码

4 `; d$ ~$ N% [0 }) ?7 V$ F4 Z5 L: @" T
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 支持支持 反对反对
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

GMT+8, 2024-12-22 12:14 , Processed in 0.137268 second(s), 20 queries .

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