模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 8 \; B9 d+ a% U6 S: H3 G" f1 n- z
Mysql中的锁语法:0 n# E3 W1 r8 K* X+ e, a# v
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
, p5 [4 B c& c0 L. i }$ E9 S0 JUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
7 k9 `" M5 P+ S2 {Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞1 z/ g; N( q' n2 c2 R% Q @" [
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :! A; N6 q5 m5 m- @' n7 O4 f X
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。/ z3 e3 I. w& y) w7 m
测试时,有个文件就行,叫什么名无所谓 总结:
- J- o0 ^8 q8 _, i1 O& e项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
- A6 w$ W1 z% M( \& E4 q1. 高并发下单时,减库存量时要加锁
# m# w6 U$ z6 z; m2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
6 R; @! k) Y* S O8 Y+ ` - 模拟秒杀活动-- 商品100件# K5 d/ ~( ^& s9 i. s5 ?
- CREATE TABLE ta
# r1 @. |7 x, [$ e - (
`# o4 ~$ j9 V. {% O) t* o - id int comment '模拟100件活动商品的数量'
+ |% U' p2 s1 _, a8 K9 O! G$ A& J J - );
7 ?# ]0 G4 ?/ k8 t( y, M - INSERT INTO ta VALUES(100);
5 L7 J5 M0 K* ` - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
% Q+ h, S9 b A! R2 W* Z1 N - */ 2 _. t+ L( p9 @+ r/ G* h
-
+ m/ f: h) ?3 r - // 关闭错误报告
) `% W o' U4 i* v1 ^1 C+ E - error_reporting(0); # v1 S2 C: |* p- X, w
- . S( j" z4 b& n2 n
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
/ y3 M# ~# D9 V5 d6 A. W) v - $dbuser = 'root'; // mysql用户名
) F2 P1 U/ E7 j% k2 _" r" d- @, c - $dbpass = 'root'; // mysql用户名密码
8 j' K+ i. x9 U/ U/ `, { - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
, t5 p9 _( N+ p- \ - if(! $conn )
4 _+ L7 j) Y3 j' j - {: j- k' [$ i3 O& ]& C* q5 |% E5 U
- die('连接失败: ' . mysqli_error($conn));
7 }6 |$ b$ `% p7 {5 l" I9 E7 e - }
: z- k N( z# Q - // 设置编码,防止中文乱码% e8 d2 _- i, A q/ [+ I- N
- mysqli_query($conn , "set names utf8");1 _4 v: V- Y3 p
- mysqli_select_db( $conn, 'temp' );
$ s* k9 J% { ^2 l% p0 w8 x$ { - 4 C, f2 ^* b4 H/ \" ?+ o& K7 m
- # mysql 锁 ) o& h; w3 u7 S4 d3 P1 s E/ f
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 5 ~- t- U: \! N7 {$ |: t# R3 ^
- $rs = mysqli_query($conn , 'SELECT id FROM a');
7 M. M' o& U. ^: }: C) n - $id = mysqli_result($rs, 0, 0);
8 G8 J! q' ]2 c" i7 R; j - if($id > 0) 7 b( {( j0 h6 c/ M# o8 R2 Q
- {
$ u: L+ y3 ?0 ?4 n) ~ - --$id;
0 o9 {: Y- ~# K - mysqli_query($conn , 'UPDATE a SET id='.$id); " W7 g& W6 r5 B- Y# j8 }7 W
- }
$ P% k$ X, {1 K, W& O8 z
D! I# w; V; n) T- # mysql 解锁 ( E1 E; ]1 ~ Z! E! B) {' N
- mysqli_query($conn , 'UNLOCK TABLES');
+ o9 y9 y$ P* A6 j% N$ G4 A) q - //查询解锁后的id值
1 }; s( u& j* |$ {& m7 M - // $res = mysqli_query($conn , 'SELECT id FROM ta');; ~+ x1 [" L+ v% L M+ B
- // while($row = mysqli_fetch_assoc($res))2 H1 O9 {# u# u
- // {
1 C& ]% b8 b; ` ` - // $id = $row['id'];
+ ]9 J# |1 R, I: y - // }) l1 \- t, Q/ ]9 I3 s) g+ v
- // echo $id;
复制代码
+ H3 K9 B6 S5 E- Y9 B6 }& H) R: p! L" C; L- `0 M
: B/ z8 P. F4 X* u rPHP文件锁示例: - /*+ s4 C$ N& V. f Q- O, H
- 模拟秒杀活动-- 商品100件
v4 j% d# Q& Y- z - CREATE TABLE ta. a4 o% E$ U+ v0 D$ L6 R
- (* K$ V- @. M; z& g) h: _
- id int comment '模拟100件活动商品的数量'
; }: _$ n/ I n2 x5 K: X - );
3 P4 f' o1 s n; T - INSERT INTO ta VALUES(100);0 z5 `$ }6 Y2 k% X2 P
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件* M3 `( x+ u6 }
- */
' n# Q5 g9 g' @$ Z! n7 Q - O; L+ l% s3 J, j% a. L' _ ~' o
- // 关闭错误报告: k$ Y' J* Q+ }4 J
- error_reporting(0); 5 V% ~) U$ [) p- ^% ?0 K8 D8 ?" W
7 p& T: w/ i7 l7 S. l- $dbhost = 'localhost:3306'; // mysql服务器主机地址
, T, T: [0 ~$ }+ q( R - $dbuser = 'root'; // mysql用户名2 E" j$ [/ O w+ U0 }0 K
- $dbpass = 'root'; // mysql用户名密码
# _8 J+ a k, G1 ^2 s - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);) a& f7 ^; F. h1 V( G0 T5 _& |
- if(! $conn )
/ U% I" j- F6 x4 |6 g1 g - {
9 V% f* O2 q! ]8 P- { D. P - die('连接失败: ' . mysqli_error($conn));- c4 w$ B3 g4 Y& E1 v& L2 R* }
- }2 G- L9 v' k8 f* W
- // 设置编码,防止中文乱码
: [4 e0 G- b) q/ I - mysqli_query($conn , "set names utf8");
) h8 u5 v- V7 d1 j6 S: R ] - mysqli_select_db( $conn, 'temp' ); ; a: }* N$ A2 W% s
- ( p9 x* r+ I( j" E) S
- # php中的文件锁 " C2 A7 H0 l$ _
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
: B" d. g1 s. B$ I - flock($fp, LOCK_EX);// 排他锁
- W6 ]) i( O2 ~) J" g) w
, r( i! |3 V, G- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 0 x% ]8 L% o/ {. J/ A8 @
- while($row = mysqli_fetch_assoc($retval))
& F5 ~# U: W( P- U - {
: u9 q" L, X* z: e& `! k - $id = $row['id'];
9 N; K- j/ n! ^# V( N6 e, m - }
3 n. N+ _/ V$ @, F4 @) R6 N( @' c2 { -
7 k! V3 y. K g - if($id > 0)
2 U0 w0 `3 o" _ - {
0 M$ Q: n4 e+ `; ~5 l4 D9 V d: ~; N6 G - --$id; 0 l7 y/ u! @ a4 O8 t
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
_, Y" K7 B6 P2 H& u9 |0 z3 g - }
# K3 J* e ` e - # php的文件锁,释放锁
" i1 A5 l" d( G6 x, v1 I" | - flock($fp, LOCK_UN);
: n/ T/ m8 e8 F - fclose($fp);
% F. X5 z2 V* j3 y* c2 N0 f" i) N( g
! m% g& e" p1 N8 A) O8 v0 d! v- // $res = mysqli_query($conn , 'SELECT id FROM ta');% y+ _" C) Q& w
- // while($row = mysqli_fetch_assoc($res))
# Q6 [8 d; B7 O) y - // {! y: Q. U' T- u+ y3 i# |! S
- // $id = $row['id'];
, T2 \4 O9 t3 B& Q% S; z' d - // }
9 `1 `: U! i8 Y L* \5 d - // echo $id;
复制代码 4 B3 B- d7 P( C" y
8 P* s6 q+ r- ~
抢券活动实例: - public function envelopeSnatching(){8 P" C$ i5 u2 I/ o& S4 o$ S" K- ~/ z
- $lingqu = $_POST['type'];+ C& t1 d& n- q# \3 u5 w/ j: G9 c
- $uid=session('u_id');//用户id- G! j# r% g% M2 t
- if(!$uid){
2 V! N1 @: o- t/ j1 R - $data['msg']='您没登录,请先登录!';. }# E/ _2 q" f$ `5 ^$ p
- }else if(date('Y-m-d') != '2017-12-12'){
* I, A8 p9 o7 E( W. i - $data['msg']='不在活动时间内!';/ Z7 X7 O1 {. G. v4 T l* t
- }else{1 p5 [% c7 |: J+ V! O! }
- $hours=date('H');//当前小时数
6 e$ B0 {1 r0 g& F5 a" h - if($hours > '09' || $hours > '17'){
3 \2 I4 X4 b2 J$ n - 6 N1 h: b/ Z% Y* n8 b" \
- if($lingqu == 1 || $lingqu ==2){//点击10点的" n* a7 A% |5 [) K M
- if($lingqu == 1){
o, _8 C: S1 r# i3 N W& K - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
! |/ Q& I2 e% y1 A& ] - if($hours > '09'){
. v! A# s6 {# ^6 `8 a& N - $num=mt_rand(25,28);//优惠券金额
6 N" B K+ {* g% d' O( e% i$ ? - $id=1;
- O; |4 _. e* I1 C% J, ` - }
" a# h' E2 E1 P3 K! s. F - }else if($lingqu == 2){
( N& S% N( l: A6 }# n* R4 X9 \ - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();. p0 \$ X! l7 O, z0 Z6 n; {! u
- if($hours > '17'){- D) X7 x# _# g, z5 i' h
- $num=mt_rand(50,55);//优惠券金额
/ d8 { b3 G3 B, a% e - $id=2;
/ h; R5 D) ]) B1 o. v- F3 g - }% m# n: d! P4 [" j" }
- }! q, R- w+ }4 f K1 S0 \" d
- if(!$id){ [, i3 R/ j x3 i" d, e( G$ q5 O: I
- $data['msg']='时间还没到,晚点再来吧。';# O/ h$ F) [! b# w/ a$ u- Y
- }else{
0 ^+ u6 Q& B }4 j. g# s9 e - if($is_lingqu){$ ~2 I8 `: j! h o( v4 _; i B' M
- $data['msg']='你已经领取过了,留点给别人吧!';5 g9 `6 ]6 I9 X% v3 m* Q
- }else{" Y& Q' _9 q+ O2 T7 x& x5 ~3 n. _! u
- //锁表. w, c% Q! }% g) N5 N
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');' m0 H3 \# p- s- U+ K) J S! {
- $active=M('active_num')->where(array('id'=>$id))->find();
" v5 v' v# n- e# g( f - if($active > 0){. k9 ~& i9 |: C3 k1 L, ~7 Z
- //开启事务2 `0 z, c0 J. O
- M()->execute('start transaction');5 X3 a$ _4 d# y" r
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));( B+ b: C! _1 Z0 Z; m+ L& z
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));, ~' v5 R: y' v# Z
- $members_preferential = M('members_preferential');# _! o" E/ E+ ]/ t5 H I/ i
- //对应投资金额,
% G( n% K0 r0 i8 ]: ]% v4 j% K - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
, E* S* x6 m3 a+ `" X - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
/ w) i* t: w, K - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间6 ?7 {* ^3 T2 u* J
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券5 m9 D. H% @# j; t' W; S7 p
- if($save && $add && $add2){" O1 C( U' S! q
- //事务提交
& g4 K3 k9 ]# f - M()->execute('commit');: G5 r2 U# O& X/ y+ d# k
- $data['msg']='恭喜你领取了'.$num.'元优惠券';3 Y) F2 s! u$ F. j. F
- }else{. T8 Z# n0 }) S( }5 D
- //回滚
9 _$ Z9 E8 }! w0 g - M()->execute('rollback');
! g) _- U8 A# g a* o4 h7 y* _/ h6 @ - $data['msg']='未知错误!';
$ [* B# j- }. T' Q3 y2 ]: i$ r - }( V, _8 X$ ]- M* u
- }else{1 Z/ v( O) L0 @: B8 ?) a. T
- $data['msg']='红包已领完,你来晚了!';
& S% I& r( |. o. T2 E" ]6 r - }- n' C% S6 u. e0 A# o# b( T/ K
- M()->execute('UNLOCK TABLES');
; j7 H/ |4 m8 @0 _: ^( B; Z, M - }
7 o2 Y" B: Q; X7 x - }. t8 t5 R! Y G) C4 i9 }) v9 K+ l
- }else{
# N# z" Q, T& d - $data['msg']='非法操作!';
" K1 l! s2 X9 l9 l. v - }. D$ x/ l2 y& ?+ T
- }else{
9 b) c( Q: O8 `* I+ i" ` - $data['msg']='还没有到活动时间,请晚点再来哟!!';2 P7 P" b5 u3 `; J9 P; @
- }
: D; q/ O: U* m# Z - }
8 Z: }3 \0 Q3 c1 L3 X& \& H, K9 d - exit(json_encode($data));- v, i$ [" w9 E1 V- _' s: [) y
- }
复制代码
* E& ?7 f5 z4 L& C9 p* `: w* R
0 z6 j3 Q {& i$ V% g0 y |