模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
/ A8 @9 ^) z9 x% z8 m: E9 mMysql中的锁语法:: E. C, @5 {$ p0 S. t
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】+ q- U& i! H* G$ o$ |0 ]
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
1 s+ r% c8 m! g* o2 L2 rWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
3 \$ p+ n/ Q8 U3 R注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
9 L/ c2 |) ]$ n文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
9 i: P- L8 C3 \3 m0 Q* @- l测试时,有个文件就行,叫什么名无所谓 总结:3 i6 S3 P# y4 ~6 l; C, F
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
0 {7 Z7 H( a( H- R/ L: `1. 高并发下单时,减库存量时要加锁
1 k% _& U& B" ^6 j/ b2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
) u0 y" `* i, r& u+ d - 模拟秒杀活动-- 商品100件8 M( [. Y$ {& B7 E/ o$ c7 |. {
- CREATE TABLE ta
+ N9 `" q+ h5 ?9 ]2 B - (: x8 D; i& Z/ X* A" m
- id int comment '模拟100件活动商品的数量'2 Y, l- Z/ \$ ^3 j6 ~+ D
- );
7 @9 x) A" X: m' y( V. c$ B7 ?" N3 M+ R - INSERT INTO ta VALUES(100);
+ S2 V# I6 y) o' P) D - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
3 d$ {$ M; f I/ l' l - */
1 _) j0 j6 {9 R' Z! k1 V; c1 z - 2 |6 f0 T) q( f7 z3 s5 l' R8 i
- // 关闭错误报告% Z) M! d0 n/ v7 _ E" m
- error_reporting(0); ) y: v7 R& L. Q2 u
- 3 {+ z9 p3 W8 U7 s* `! z
- $dbhost = 'localhost:3306'; // mysql服务器主机地址9 l- O, c0 H S# ]# T) I" r5 x
- $dbuser = 'root'; // mysql用户名1 }1 O6 K: f8 V2 U/ E4 G
- $dbpass = 'root'; // mysql用户名密码, ]& @4 }+ d" j/ M. Z S7 c$ x
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);$ u8 [4 Y1 X9 E% ?2 p
- if(! $conn ); c# J- U1 ~$ J& G$ S# N7 |6 A
- {3 o& `8 s" D/ O* p# O8 l6 D
- die('连接失败: ' . mysqli_error($conn));/ N3 \- v5 Z, O/ y
- }8 o1 v/ c/ q5 `" M; x1 C
- // 设置编码,防止中文乱码" _8 m/ M" z( D% m
- mysqli_query($conn , "set names utf8");
* Q1 b, a9 h7 @: q9 h* K$ u - mysqli_select_db( $conn, 'temp' );
' d1 H+ b+ \2 k2 I - . Q* y7 t& ]7 e3 ]
- # mysql 锁
8 g9 t# e9 T4 y; u' u - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 * v+ w( T/ A$ P1 z/ o- T% @
- $rs = mysqli_query($conn , 'SELECT id FROM a');
$ M ~" j9 R6 o+ }5 ^$ C( [5 j) @" A - $id = mysqli_result($rs, 0, 0);
8 K v" N) h5 h - if($id > 0) + j9 t+ m5 M9 V Z5 |
- { 1 T9 b9 W1 T0 s
- --$id;
# _; O, T; e* ?+ q2 @- d - mysqli_query($conn , 'UPDATE a SET id='.$id);
- u4 l6 U8 K4 d; R; c - } ! Y& u9 e# j- [8 t! |9 v( d1 V
- , P, w: h D4 A5 {. H9 |+ k5 A
- # mysql 解锁
) k* l9 o- y+ H* N& c$ _, o - mysqli_query($conn , 'UNLOCK TABLES');
7 i. l" l& w0 v z; M' _- m - //查询解锁后的id值/ s9 n* b4 a6 A1 Q5 @- [# Y/ {3 G
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
7 M- n. R; L$ p9 Y. L/ ` - // while($row = mysqli_fetch_assoc($res))" @& \6 b! m( P x6 a! X- G+ x
- // {0 d1 D; o! s0 d2 X, t. r
- // $id = $row['id'];$ v. k! W8 s1 g, |' {
- // }
0 [1 w7 K7 l; _ - // echo $id;
复制代码
0 f- h1 E( A! H0 p
: G3 Q' ?3 S/ V5 O' M2 R# d9 X. c% o. p$ D, `
PHP文件锁示例: - /*% i4 c0 n( k9 m# c) h5 R L& M$ h
- 模拟秒杀活动-- 商品100件
5 V0 P! A) j) F6 v4 D1 I j8 ? - CREATE TABLE ta
1 ^9 n( n4 _ h4 Z) w: w1 I - (
2 \6 w: N9 Z0 k- ^% A) J( T0 z& j - id int comment '模拟100件活动商品的数量'# a$ j* j+ b, t) R: K9 i8 B5 O! P
- );% V/ j# z5 `3 h7 j; S' w1 Q+ E8 O
- INSERT INTO ta VALUES(100);
) a! v- [7 }- R2 L4 k - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
, q4 ]( n3 d2 S2 R5 K# Q - */ 1 s h" ]4 u& r8 R; L# ]3 g
- ' z4 X7 s2 P" j( ?# y
- // 关闭错误报告& C6 e1 R% l$ M" b# t
- error_reporting(0);
~% E; N* h6 e9 A0 K - ! r3 X! N. @9 m" B: V" B0 S
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
* n( Z! a' v2 L) M - $dbuser = 'root'; // mysql用户名
& r5 Z1 W: m" }; S- Q- o$ O5 r) t - $dbpass = 'root'; // mysql用户名密码9 M; g3 L" v$ \$ b6 N
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
( t# N; }9 N4 l - if(! $conn )$ O5 G1 _2 c: e) l- s$ K' P
- {' h' [% X8 L: E1 u* L X
- die('连接失败: ' . mysqli_error($conn));
- ^4 M$ M0 U' w) X5 G' j4 c1 Y - }
& o5 Y+ r: A- J - // 设置编码,防止中文乱码
* K$ L5 b- @% P n% A - mysqli_query($conn , "set names utf8");
* F$ Z# k) X% l# p - mysqli_select_db( $conn, 'temp' );
/ ?5 s3 z* h0 S% p) i/ C9 j9 s3 z0 s - : D2 W [4 a8 l0 R2 T0 \
- # php中的文件锁
/ h4 p0 {+ M$ p. E0 h - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
6 r$ H5 g" Q! f$ X+ A/ B' H2 K - flock($fp, LOCK_EX);// 排他锁
+ T0 `2 n+ D- P: B& V* d
% w4 K% Y8 S9 s" ?( i' g, [- $retval = mysqli_query($conn ,'SELECT id FROM ta');
0 |& m: u1 j7 r L - while($row = mysqli_fetch_assoc($retval))
* d0 g6 u; a* g - {# \- Z- ^' ^1 E- Z' k2 v5 x- G" \
- $id = $row['id'];" L }+ l- |8 B: S
- }/ C# j( k8 S/ s6 }' J
-
1 V. m6 O$ p$ Z* f3 z- E! o - if($id > 0) . `) v" S; v! F7 _; L; R' M+ H
- { 8 Z* Z/ c/ h* S1 t* x0 q" Q' h
- --$id; 8 V$ u0 P- g" b
- mysqli_query($conn ,'UPDATE ta SET id='.$id); 5 R7 Z+ `# b; `
- }
8 j2 m( j3 W6 F$ b% v - # php的文件锁,释放锁
! M. o5 Q K: D( n# O5 V6 g( f - flock($fp, LOCK_UN);
; v, D& g: @' h I/ K - fclose($fp);5 x( o/ i& m; m( H/ X% ~
" _* {* S7 O2 Y" N9 Z+ z" s- // $res = mysqli_query($conn , 'SELECT id FROM ta');
$ G! b% V/ K, ^" C7 V) l) Z - // while($row = mysqli_fetch_assoc($res))
^: T4 V# f2 A7 ^1 ~ - // {/ D0 U M8 z8 d( n* o# @* z" y- f
- // $id = $row['id'];: \) C, c+ U/ Q
- // }% S0 g& X" u% x/ M( q$ M) T
- // echo $id;
复制代码
. [0 |5 m& E" _
$ Y7 Z, V5 o- R+ m5 b抢券活动实例: - public function envelopeSnatching(){! T7 z+ p5 m6 h$ J5 o
- $lingqu = $_POST['type'];4 q w: G) q5 O7 }
- $uid=session('u_id');//用户id
; n# v4 u+ n* ]- Q+ Q# }1 y - if(!$uid){
* a) K( |) V ?! D7 @$ C - $data['msg']='您没登录,请先登录!';* Q! K- H, R8 _4 B2 W
- }else if(date('Y-m-d') != '2017-12-12'){
7 ~& _6 h) b# r& x. f- s - $data['msg']='不在活动时间内!';: ]/ ?' P. _& H5 H4 n
- }else{$ s. C- q; |4 ?: {8 f6 ^* F
- $hours=date('H');//当前小时数2 J+ k0 b: ^+ ]6 W3 A/ Q
- if($hours > '09' || $hours > '17'){. }, j% P6 H9 i
- 2 c6 C6 _. Y1 k* {6 a5 K. g9 N0 \
- if($lingqu == 1 || $lingqu ==2){//点击10点的
6 z# q" n# j# | - if($lingqu == 1){
0 q4 u! D. O5 x - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过* A- F! Z: v! q, g! ]9 h
- if($hours > '09'){
! y$ z9 T/ Q. l4 a - $num=mt_rand(25,28);//优惠券金额
) m) p4 y+ t) r7 {, Q3 X - $id=1;! w' q* v8 ~1 X! s, p' ~
- }
0 L2 J8 a+ v5 u# f - }else if($lingqu == 2){
1 c H* f( j. y" [ - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();! K- e2 v; H9 o" X# K7 s, L
- if($hours > '17'){
% K/ }( F q2 k- z7 ]& ? - $num=mt_rand(50,55);//优惠券金额
* f/ ~) g+ I! P* Z8 x: t3 U - $id=2;+ z& Y! b$ u& Z) _1 k
- }
/ G* Z- R! l! O8 c - }
0 C* _' e: h: ^4 I4 [0 T - if(!$id){
4 }8 Z( d; D$ K# ?) Q - $data['msg']='时间还没到,晚点再来吧。';
; v5 X9 m/ Q- X - }else{
: q1 L9 I( Z g- E5 F/ S - if($is_lingqu){
( I8 O( O; T6 B" y - $data['msg']='你已经领取过了,留点给别人吧!';$ S9 }: u' ~; q3 c4 g* Z8 }7 c
- }else{+ q2 z# g1 j6 z+ S, p
- //锁表3 T" c; }8 R6 n7 q! o% I
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');: t) `# D: k2 ~( @2 C# M& C" w
- $active=M('active_num')->where(array('id'=>$id))->find();
# b* t* v' f q* j9 D - if($active > 0){+ o: G8 A' H" U: Z; g& ?
- //开启事务
% @- {! P3 f; S1 c3 { - M()->execute('start transaction');. c6 h0 o |6 `# i0 _2 T4 G
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));" B1 c$ h9 z2 X* r* |
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time())); w" u( ~% R+ Y3 M# e# |( N
- $members_preferential = M('members_preferential');
, u+ m- C7 j- ]1 M. E4 z0 Q - //对应投资金额,+ Z) H: K7 l& A7 j6 Q
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));4 Y/ c. h7 X. @2 K
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');+ J0 F# ~6 I7 d; |' O9 I* C4 p2 U
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间! O, E' W9 Q: `3 ?- C% M
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券6 J4 L) Z! e4 K9 q" ^
- if($save && $add && $add2){# V- p6 T4 ^2 U
- //事务提交, e8 _3 \: w1 H# `
- M()->execute('commit');5 y# A, q2 {7 S/ \3 e% W# o9 F4 k
- $data['msg']='恭喜你领取了'.$num.'元优惠券';. b' l& a0 F3 m3 _! K( p
- }else{
: p1 n5 y+ s' D1 x% |9 p0 S - //回滚8 y4 I/ Q+ l& C" ]9 E* I
- M()->execute('rollback');
( _7 f5 r: {! \ - $data['msg']='未知错误!';
, V$ I6 @6 X, h1 B - }
C0 ~7 Z" H3 n/ m$ r - }else{" @4 |& S; y3 G* n) T. ~' C) h
- $data['msg']='红包已领完,你来晚了!';
5 T, K; `4 x2 |6 O - }$ o- @! G+ _2 K. p" A: F" o# k% m
- M()->execute('UNLOCK TABLES');
# X9 L, ?' h0 v( `& C - }: E D7 @7 q( O- _
- }, F( o1 [! {* v" S7 t* J* r
- }else{
- U4 W, q u; P, P, B6 a3 Q0 d - $data['msg']='非法操作!';
, B7 C/ z1 m. g' c ]* Y - }1 _6 o- |: I9 {, T; h1 e' P `8 D& f
- }else{$ K9 f: }- x5 Q4 w7 J; D
- $data['msg']='还没有到活动时间,请晚点再来哟!!';; J! x6 }! n5 ^3 s( V
- }" |5 O) F! I7 N' K, M" N2 U
- }
' s* Y4 v) G9 d - exit(json_encode($data));0 l8 E" y! e3 x* W q# c* }6 D
- }
复制代码 4 W2 I0 q* L4 S: w" i
0 X2 f' `4 L( Q" n* L, o, k |