|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 0 [: e* R( w0 I# w" q4 i
Mysql中的锁语法:# [" F' O0 U% p1 r4 e
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】9 N, W* R6 L) b
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
+ m" o( Y8 x5 m) z' TWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
% {/ |! X/ ?( O( n& p注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
3 `, C: \% V( M! z, Q+ P9 i文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。6 z9 C9 C, b% T" H( t9 K
测试时,有个文件就行,叫什么名无所谓 总结:
6 m) I8 J x5 w7 v项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:+ F- S7 m* j; L" b
1. 高并发下单时,减库存量时要加锁
1 B3 l1 a" J; f/ b3 E$ y2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
7 ~* R; _/ j# }) X# V - 模拟秒杀活动-- 商品100件8 t! K$ e1 g+ U1 Q3 t
- CREATE TABLE ta
/ ?5 ?$ a& i2 t8 I1 e ^/ Q - (1 P7 Y- w' L6 T6 y9 S
- id int comment '模拟100件活动商品的数量'4 _' ^! T; W' U# |# |: Z( \2 p
- );5 T* a, N# @* c% W/ w! j
- INSERT INTO ta VALUES(100);
: I6 p& Y8 [4 K% { - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
: Y1 ^& c1 y% [9 P0 T - */
8 h. C- _/ `3 @1 {9 e -
% ~5 h3 N; k- E0 r7 ^ - // 关闭错误报告
: D8 c/ c3 F' _! i, t0 \* x - error_reporting(0); # P) J+ |( e9 ]
( @& u4 W2 W3 r* q# {- $dbhost = 'localhost:3306'; // mysql服务器主机地址
+ s0 L* v) _. ?. s - $dbuser = 'root'; // mysql用户名
# Z" X2 K. H' M7 m; N( ^# B - $dbpass = 'root'; // mysql用户名密码
, A( e$ N: r' B4 K3 M - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);$ R6 T; [% G8 u# u8 b
- if(! $conn )
# ?9 ~) w& R2 d$ K$ h - {
3 J. f' k0 `) \6 d, x6 Z, {4 d - die('连接失败: ' . mysqli_error($conn));4 C! K4 o1 }' n4 c$ U
- }# l( L0 p' A+ N1 e, V
- // 设置编码,防止中文乱码
2 Q# J0 m' J8 G% n5 R5 V4 T - mysqli_query($conn , "set names utf8");
+ T) t+ D Q( p* ]1 Z- i1 ] - mysqli_select_db( $conn, 'temp' ); ! z) I8 k0 w. p% R6 C! q3 a$ P8 ]
\, x3 p8 W1 g0 I* I F- # mysql 锁
* M3 [) D( e# x; v - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
+ b' e1 h6 {! z! t/ r - $rs = mysqli_query($conn , 'SELECT id FROM a');
% C) R$ g Z9 h( C" C" x - $id = mysqli_result($rs, 0, 0); - X% U5 Y5 E3 A" @; h# h( f
- if($id > 0)
5 i- E% a2 ]4 y6 y9 B9 s3 Z# X6 I - { 1 G" N( C1 m' y
- --$id;
' \: }) e3 k5 b# T2 x8 K* Y - mysqli_query($conn , 'UPDATE a SET id='.$id); ( E, J$ `" f9 ]6 J/ h/ f
- }
) A2 [9 U" U" b9 G( _: T0 E - ' |+ N# g0 H6 j) }
- # mysql 解锁
+ x1 }: B9 g" K9 d; R8 `" G - mysqli_query($conn , 'UNLOCK TABLES');
3 z6 o5 {+ K( C. f, Y- a) \1 G - //查询解锁后的id值
4 d$ A4 ]2 H: ], O: Y, l+ j - // $res = mysqli_query($conn , 'SELECT id FROM ta');
! z3 ?3 W5 ?, d1 Q% A; S1 i - // while($row = mysqli_fetch_assoc($res))
# X0 }& N3 d: @3 s! J! V! p - // {
g' a- U R* N! J& n( c$ { - // $id = $row['id'];
) a& ^6 ~/ V) _$ |" U - // } s% T' o9 r' m' l5 j
- // echo $id;
复制代码 0 I2 H3 O8 ]2 N% v; @( i' H$ n
/ ~5 s" g9 n) i" u
0 v( n' m$ A ^4 Q# L* b
PHP文件锁示例: - /*
' X, [) j- T& D% T- i - 模拟秒杀活动-- 商品100件: A9 u9 O3 X& @; b1 Q5 w
- CREATE TABLE ta8 i1 M& N$ H9 K6 ?0 X% z
- (
( ?* e/ X) p. X( n7 x - id int comment '模拟100件活动商品的数量'% ~5 p9 X7 T5 t2 U
- );
+ } ^2 @, m7 a8 p& q- u9 N% A - INSERT INTO ta VALUES(100);
# J# J4 [* _9 u. b- m# z2 [ - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件/ }0 ~2 r4 C: G. }
- */ + \6 m4 L: j( @$ J( [. f+ q6 u& w
- $ ^3 u2 D9 {" K8 W5 c' G
- // 关闭错误报告, b* x2 i. Y4 `9 s. k
- error_reporting(0); : D }3 s, W y0 O/ t% r
- & i# s' f' H1 q4 U
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
& ?+ D: _ f! S K* ~ - $dbuser = 'root'; // mysql用户名- I6 _/ f9 a3 B: @! u# d. p
- $dbpass = 'root'; // mysql用户名密码/ {' f9 m% |' g! s+ a
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);. {9 w: i$ T0 V0 E, L$ y ^+ J3 q
- if(! $conn )0 f N5 i5 |* ~* }( Z4 H' g
- {- h, W& w4 e: Y: E' N; `; B
- die('连接失败: ' . mysqli_error($conn));
E3 ]0 C7 s& r7 t, b, f0 D - }* \" P3 J: p% @2 r
- // 设置编码,防止中文乱码
$ G, {6 s/ O! N1 M" M+ P - mysqli_query($conn , "set names utf8");6 i0 d' d. r) [
- mysqli_select_db( $conn, 'temp' ); 4 N- z& A5 O6 a& X' y1 A# r M
$ g4 h9 `; |6 J+ o# J: T* M& M- # php中的文件锁
9 a: O1 ^3 |* I6 }$ J$ D' \6 }' V" m - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 ! l- z6 C* M0 {) S7 v
- flock($fp, LOCK_EX);// 排他锁
" d" `; F. F* R) x2 O( O) g
8 w9 s( c) U# u: i- $retval = mysqli_query($conn ,'SELECT id FROM ta'); ) b' _& I% l O8 f1 h
- while($row = mysqli_fetch_assoc($retval))
, Q* z- l+ N9 M* t# t7 C - {. o5 @, D, H. S% N& _, [
- $id = $row['id'];
9 b7 ~+ J" ]. {: \9 V - }
! _3 m( w$ `4 H4 f -
* k1 f& E% Y: h/ n - if($id > 0)
4 L/ P) t' m7 }' u6 S d; J - {
8 W6 _3 z! d2 b - --$id; , b2 X4 z( \% v- ^, ~9 a" q# ^
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
7 D* s1 `( f; X - }
# m* b; `0 r* S z - # php的文件锁,释放锁 0 J, B4 p6 e7 W' d/ C2 N' Z5 _3 X
- flock($fp, LOCK_UN); 4 x2 U/ r' E3 F1 K" R( v
- fclose($fp);
6 I! P2 z+ _8 p# @& ]
% U& \! ^9 G8 s8 d2 |, e- // $res = mysqli_query($conn , 'SELECT id FROM ta');4 B( G7 A! }* J' s
- // while($row = mysqli_fetch_assoc($res)); Y" ~ @# d A1 ^, y+ a- }
- // {3 ~ I9 z3 ]' a3 i( _3 v* M
- // $id = $row['id'];# }) R$ |+ r: U- Y0 m
- // }! h; l& h# Z+ j
- // echo $id;
复制代码 / k+ q/ R# @. I- D8 R- r
; E' I- n% r. e
抢券活动实例: - public function envelopeSnatching(){# |! A, N# v2 f7 q: a
- $lingqu = $_POST['type'];7 ?9 l2 u/ r- n$ X% h! ~ N
- $uid=session('u_id');//用户id
" i- {! w% I" T+ I - if(!$uid){/ C7 ~( K7 f( u- o5 i1 @' I$ I- Z
- $data['msg']='您没登录,请先登录!';
0 _2 k; s! R6 X- ~4 j+ v - }else if(date('Y-m-d') != '2017-12-12'){' R" S7 c3 @) n$ M0 l
- $data['msg']='不在活动时间内!';
4 m {+ Y* @+ y) v - }else{
7 W9 Y: R6 }$ M - $hours=date('H');//当前小时数
1 h" C8 q4 N2 }* Q' N! ]( K0 n1 { - if($hours > '09' || $hours > '17'){/ x; O" U* E9 P- H6 f7 P9 }
- $ V0 j; b# D. w* u' ?% O
- if($lingqu == 1 || $lingqu ==2){//点击10点的
9 A% f9 c, x3 V3 i0 U( p" o - if($lingqu == 1){; X: n- J. N$ W
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
I" B1 ~& B R* H+ D1 X* ` - if($hours > '09'){
, X& h/ C" Q' k( E - $num=mt_rand(25,28);//优惠券金额2 t# P) a) V2 P. A! _, I5 {
- $id=1;& k i0 e! d+ V5 L1 d( Q
- }$ d: U3 X: x9 p, J% U
- }else if($lingqu == 2){
( u E) s0 ^3 {7 o - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
# {5 w+ I9 t) A5 I J$ G# ~, q - if($hours > '17'){
5 n F$ r- N0 h& ^9 Z/ a. }$ m - $num=mt_rand(50,55);//优惠券金额! [1 B2 k1 |) [& R' a- A5 D: I
- $id=2;' o6 ?9 X* u" @* f3 [ `
- }
, S1 L- s ]9 c+ P2 I - }/ s$ w5 | D$ W( v
- if(!$id){
7 B, @' e0 S* v8 h; o m8 C - $data['msg']='时间还没到,晚点再来吧。';# |+ p X. z/ y
- }else{ G$ X1 r9 k& c9 l5 H
- if($is_lingqu){. @) r4 `' m. B( a& b
- $data['msg']='你已经领取过了,留点给别人吧!';* [; V. v: w1 q" U& Z1 J; ^$ w
- }else{
; I9 I0 w4 H3 Z' C- q8 N& W - //锁表- o; v7 ^ o) q$ i" m; Q* T f5 T
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');$ P7 j- x6 E6 j$ d$ u9 A# Q
- $active=M('active_num')->where(array('id'=>$id))->find();; i4 o: e3 f& Z. M3 o/ u( c
- if($active > 0){2 t4 Y) D4 X; c# z5 R- a
- //开启事务
& ]7 K3 A- H T - M()->execute('start transaction');; A. f8 k7 h O- c' L! Q! l
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
8 t/ a, T4 D# ~ - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));. e/ A" S) \: ]5 @! n& @+ _% w& q
- $members_preferential = M('members_preferential');( @# D/ U! V* U2 U
- //对应投资金额,
0 H. M1 R& w0 [: j" w - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));6 M9 ], E' o$ t O" t* S$ o
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
! F+ Z3 A3 H; _/ C - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间% [% A* t$ U3 @ j- p4 c2 u
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券* ]: T) ]& L4 ]5 j5 }5 U, j3 i
- if($save && $add && $add2){
/ _& h3 b2 w$ h7 ?# u N4 v - //事务提交
5 l U: C; x" S - M()->execute('commit');
( Q) ?" }5 B# ]( [4 o - $data['msg']='恭喜你领取了'.$num.'元优惠券';
4 C: M+ E$ c% v - }else{
4 q0 T; @8 l& g! o8 Z3 r9 D3 o1 \ - //回滚. [- Y% B& m/ c
- M()->execute('rollback');
8 v5 P* g6 v6 ^& G# z - $data['msg']='未知错误!';" R4 H' b/ [7 g( s% f) g
- }
( }4 N2 D# P5 P. J - }else{: S* E2 X: v* ]% J$ [; D( y0 k; G: _
- $data['msg']='红包已领完,你来晚了!';. z: O G& ]4 U0 Z0 D7 ]
- }
; F& r, W! |+ N! f - M()->execute('UNLOCK TABLES');
+ g) W! _5 d0 E3 M k* k' Y - } o3 n7 z& }7 H3 Z
- }8 f' V1 u# h! G. c* ]8 ^5 P
- }else{) s" k2 [1 F2 z6 K! Z
- $data['msg']='非法操作!';
2 a i5 B/ R9 r8 g; ^4 B - }+ I. x r/ I3 K2 T1 V z; j
- }else{: X1 k. c( o3 a% B6 z& c$ q
- $data['msg']='还没有到活动时间,请晚点再来哟!!';1 Z/ @: Q5 |: O7 l; _# z) w
- } }' @) Q+ F0 {% o2 H5 {/ Q4 Y
- }: Q) V2 X9 l1 r5 F0 G; ?1 M3 b
- exit(json_encode($data));. O* d' U" A0 H6 Y8 g0 b( A7 \
- }
复制代码 & `/ N" n3 }' f7 D2 \
0 {8 Z% o2 X% N j |