模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
6 h, U5 b! q+ Q3 M' {3 J5 q; fMysql中的锁语法:- }) j) O4 T3 P0 q
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
; N' V& t5 i1 vUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表$ Y! S& P* N T" m& w
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
4 a5 E5 n0 p9 S6 S' ^: [# N注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
8 ]/ n/ `2 a" C! W6 @文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。1 f9 o# J% `" t( X. G. ?$ L. e% c
测试时,有个文件就行,叫什么名无所谓 总结:$ Q7 j' D% o u1 ]+ _8 x
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
: I0 X3 R% P4 g* E( G1. 高并发下单时,减库存量时要加锁
% f% X# {8 V4 @+ r5 F2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
4 G, C/ W" o2 k( z, ]& ^ - 模拟秒杀活动-- 商品100件' v, x7 F- ^8 q( _ H# \
- CREATE TABLE ta: t* D/ j5 d" S) q z+ }: r
- (
. l# d; d1 d' l( W' x - id int comment '模拟100件活动商品的数量'. ]3 U1 g/ @1 Q7 z
- );
8 t6 E8 n* _& I, ]. Y1 U* ~! s - INSERT INTO ta VALUES(100);
6 K% G5 _% ]* o5 V f5 k - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件' {2 ?, x$ Q T+ X, R) n
- */ ; w3 P$ G' n3 o+ f
-
" k1 \- k0 W$ x+ Q- ?; l - // 关闭错误报告
$ c! V8 z- w x' D+ p, h. i - error_reporting(0); ( e: r4 c3 N7 e5 [
- , k) W) ~) d3 `* z8 E
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
% t2 k+ D+ g5 ?' H4 @8 p! ]3 ]1 G9 ] - $dbuser = 'root'; // mysql用户名
; S- L% T2 i% T7 ~) @. ` - $dbpass = 'root'; // mysql用户名密码7 d0 Y, h/ W4 ?" \2 e( i
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
. y. s/ Z7 I0 M% u0 [2 ? - if(! $conn ), D; p* x4 ], w. M1 f! U. t- y
- {
! {( L* X. p& b$ J- x" A1 R - die('连接失败: ' . mysqli_error($conn));% _2 T V: l2 r
- }: E8 Z- k& ]' {
- // 设置编码,防止中文乱码6 C( ]0 S, O- O- R0 O" m/ j+ N& p% P
- mysqli_query($conn , "set names utf8");
) n+ Z0 G4 c0 @$ B. ] - mysqli_select_db( $conn, 'temp' ); . V/ w2 S; Q# k
- ' {7 R) A1 z/ T, X
- # mysql 锁 : D+ l% E, L/ v8 O% C3 q* i3 R
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
0 R4 l5 s: i3 ^+ A4 V2 y0 X - $rs = mysqli_query($conn , 'SELECT id FROM a'); 3 [" { u& L1 \* u! E- j
- $id = mysqli_result($rs, 0, 0);
! T' P' _6 T n$ |' B1 C - if($id > 0)
" U+ X5 Y% K# ?% G. c& m - {
. s* K$ n. Q+ F/ D' t - --$id;
1 T8 D3 X) z% r% }" x; p- V - mysqli_query($conn , 'UPDATE a SET id='.$id);
0 ]2 @" l% }. I2 p$ x - } 1 l" c- w% k9 i0 G
- " G2 T: {8 q) |7 `2 C
- # mysql 解锁 6 Q. V7 C1 T6 g
- mysqli_query($conn , 'UNLOCK TABLES');- d* h/ D$ p3 f! N9 j
- //查询解锁后的id值' X' l: |( z; N/ b: V1 M
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
5 z6 x' N* [3 g" J - // while($row = mysqli_fetch_assoc($res))& o0 R9 `! Q/ ^- } L
- // {2 z$ y) G4 `9 r! y1 U
- // $id = $row['id'];3 P+ x. J; Z6 J0 u8 W
- // }
" P9 y7 w8 `; d( v+ N - // echo $id;
复制代码 + f8 K% Q$ `( |( I" Y
" v% s0 c+ w8 L( t6 g: K: Z! ?( }- \/ k e E( c5 S0 g
PHP文件锁示例: - /*
/ G2 g1 ~7 } m' K6 V4 ~6 \: m - 模拟秒杀活动-- 商品100件
( e! G/ D5 K& }& w* z. j2 ^- Z! h - CREATE TABLE ta
* v- a9 G0 m1 s) A - ( ^' F6 ~4 c3 [* k$ B' R
- id int comment '模拟100件活动商品的数量'3 j' {3 y! R6 i* T9 F# Y
- );
% i. t% }( b6 }, r2 d - INSERT INTO ta VALUES(100);
2 E/ x. W. t$ R: t; x% ^2 M - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件5 ^5 x. V8 T5 S: ^% I9 _# B
- */
, m0 y' Z0 ]& G) [' D3 z - ; K! y% X2 m$ ~
- // 关闭错误报告
) G5 {! \9 } A; J, C4 A! a0 g! c - error_reporting(0);
; P: N9 j6 M2 G# S7 p: a
. i0 I1 }+ ]: `- $dbhost = 'localhost:3306'; // mysql服务器主机地址
/ Q+ K( b7 @; J0 Y: J! ~) l) { - $dbuser = 'root'; // mysql用户名9 q1 s5 S3 S; e2 {% t' d( u
- $dbpass = 'root'; // mysql用户名密码
8 K# M- R4 x% I - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);0 m4 b, G7 y8 X/ u5 m5 j0 h4 w
- if(! $conn )
% k) n& L/ _4 o/ C: t( C8 q' K9 x - {
' a; H+ D3 J. u1 n" B/ w - die('连接失败: ' . mysqli_error($conn));1 a6 C! z* \: |1 T0 p8 m5 @6 r4 p
- }, t5 k g+ H) \: x2 A J
- // 设置编码,防止中文乱码! L7 q/ P1 O4 I2 n2 `
- mysqli_query($conn , "set names utf8");
% \- w! z6 R% ~ - mysqli_select_db( $conn, 'temp' );
+ C; \" m. ~. c" n. }/ h - % b1 D. \1 [+ Y. y& u4 K! T6 e
- # php中的文件锁 $ y- K. V7 r3 g
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
( ^8 l8 }) x; w; U% E - flock($fp, LOCK_EX);// 排他锁 ( P2 [% K. `1 s. b( s
- 9 _( q. I- D- r8 W6 F9 k
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
, P+ t1 L! i6 R! n8 h - while($row = mysqli_fetch_assoc($retval))
* t T1 f3 H1 G- C1 ^ - {
8 A; H( q$ E9 j0 _7 Q; Y - $id = $row['id'];' ]- K+ F* W- |4 m4 s# ?: s
- }
( L9 x7 |# t" ^4 w -
5 C0 ]/ b( z7 @5 F - if($id > 0) + |) b" k4 f* k4 h
- { + L8 x) z2 n$ P* t; k! D
- --$id; 5 a, K& ^( V: I
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
: n$ Y; o9 y+ w% A) O - } , ] s% r1 {: h9 ?0 ^. n' K
- # php的文件锁,释放锁 % P. ~/ D @/ |5 w! M- W
- flock($fp, LOCK_UN);
0 `3 `; ?& `* L A5 V - fclose($fp);
$ ]) F& z( c% {
3 s8 A: V: C! ` g- // $res = mysqli_query($conn , 'SELECT id FROM ta');. ^: m4 `& j: r: R+ P2 u9 ], v
- // while($row = mysqli_fetch_assoc($res))
: b: \5 D1 P6 o6 v1 P* G - // {
6 {1 I" U7 p; e4 } - // $id = $row['id'];* w: v7 \+ ]5 z+ N) b3 n6 _
- // }( W4 u7 o4 x% C5 q
- // echo $id;
复制代码 4 x, m2 f0 n3 |# \8 W( G7 Z4 Q; q
1 {; W% {; ?2 I
抢券活动实例: - public function envelopeSnatching(){ Z$ o2 c" b0 g1 Y6 V% Q& u7 q
- $lingqu = $_POST['type']; ?5 d U" p+ q* H, U" |
- $uid=session('u_id');//用户id
3 k0 |9 t a- K! C2 K8 I& J - if(!$uid){) G5 U8 k6 e1 Z& Z3 A' q& d
- $data['msg']='您没登录,请先登录!';0 @* k* `' g/ H+ J* e
- }else if(date('Y-m-d') != '2017-12-12'){4 B2 ?. Y/ E4 o0 V) C
- $data['msg']='不在活动时间内!';
6 u [; {% X& G - }else{- G3 f. |( b6 B" t# a5 K
- $hours=date('H');//当前小时数
8 a$ r; J" a* F- M/ l - if($hours > '09' || $hours > '17'){6 m8 L/ D2 b' k9 W: q( z' y, G
- c8 n: L2 R5 T p2 e4 q
- if($lingqu == 1 || $lingqu ==2){//点击10点的' J* B9 s- g: ]* d0 Q; ]- c
- if($lingqu == 1){
4 B* u$ t* H6 @$ o, i# P2 g% C1 a' b - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过# y* N+ [: ~3 r( @, f! t
- if($hours > '09'){+ o' h* y% M% ?4 s7 r3 H; b
- $num=mt_rand(25,28);//优惠券金额
9 M) Q) e5 e- p( y - $id=1;
; a" Q5 m' a- S1 X, @5 Y0 b - }6 Y4 S* d( C' X1 d- c8 F+ w8 F6 B
- }else if($lingqu == 2){' N# n% E4 @4 ^4 E; i( E8 I6 G
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
% w" a( D4 q6 f! C* c2 _ - if($hours > '17'){6 g' ^5 W/ F6 g' H r9 D
- $num=mt_rand(50,55);//优惠券金额
) A2 N( X, Q0 p4 D2 ^8 N. Y7 C, ~ - $id=2;
; ?/ |3 X( P% ?8 C - }8 y+ e" I2 j: ~ I7 u2 N& ]
- }/ T- r q3 m1 L- K. ]* E! T
- if(!$id){% J9 Y$ F+ P S( o
- $data['msg']='时间还没到,晚点再来吧。';
/ @7 H' V$ _6 _ - }else{
# z' ` W+ m, n - if($is_lingqu){
- M( ~! J# q3 f1 z# P - $data['msg']='你已经领取过了,留点给别人吧!';
M, X/ B) i, X6 o y - }else{9 X0 I7 S% w# A) r
- //锁表
9 E/ K: U [% c( t/ G& a - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
7 S: [% r: \' _ - $active=M('active_num')->where(array('id'=>$id))->find();
$ u% y2 R# _6 U; V) i - if($active > 0){, }! ^/ J7 X- J& R' V
- //开启事务
. m% q2 W6 A+ ?0 u- W) G - M()->execute('start transaction');! @1 |/ ?7 L z# W' l- b
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));$ K0 r+ w# u+ L2 x
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
2 |1 K" Z- `, _7 A5 B8 Y3 z - $members_preferential = M('members_preferential');
9 s1 U' s! j. M* u+ z$ M; j3 f R! ^' s3 ~ - //对应投资金额,
- Y ~# H- k" Y, J - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
$ D8 p# n3 F$ ^: P6 X, y/ y/ q- w7 a% B - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');, d& C! T) h8 @, ?
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间: S( x# q) x/ V% N# u
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
8 G; c X7 M8 ^3 @ - if($save && $add && $add2){
4 D$ Q4 v. ?+ O* y0 C7 {! E1 f - //事务提交
7 x5 O/ x% G; k1 o2 M5 K - M()->execute('commit');
D( F* l( o& N: q5 b; R - $data['msg']='恭喜你领取了'.$num.'元优惠券';+ N1 w6 F' {8 B) f, A& J3 F
- }else{( A( R5 I2 w* ~+ i9 f; t6 D! J
- //回滚2 Q; C' J" g8 e5 M' e8 `; w% X
- M()->execute('rollback');9 @' [# h/ ^- |" w f2 V8 Q2 M( p
- $data['msg']='未知错误!';
* t( r9 x9 y! [; i- u4 G; R - }
1 G, ~; J, I. z9 P* s - }else{$ N9 \0 B6 _5 W1 ~* v0 [( O
- $data['msg']='红包已领完,你来晚了!';* c) ]8 D/ H" p0 {
- }
. @5 y- ~, K; S+ F, L. G; T. \& ~ - M()->execute('UNLOCK TABLES');
8 L2 X, J5 I, a4 q - }
w/ \0 A4 e; A; S( v: T - }
: ?' \+ W" y! J9 J; ]) ` - }else{ {! j) h9 n$ A& H- e5 d+ v- @
- $data['msg']='非法操作!';! f. z" I9 w* d8 {
- }6 \6 K+ S- o0 @- B Y" |
- }else{' u0 ~0 v+ J4 U2 ~) J9 ^1 |
- $data['msg']='还没有到活动时间,请晚点再来哟!!';" s2 u P& B+ c o, ?' p+ | M
- }
8 J; a6 O# X( _1 b3 u6 G - }3 C! E: L% x4 t; u1 x4 I
- exit(json_encode($data));
; r/ s3 r- d! y! q% h+ ^ R - }
复制代码
% a: b' x/ ]9 H) f: D( ]9 U" Y& H# L( s+ }' _
|