模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 ' O' M4 ?4 ?5 I" U# _6 S
Mysql中的锁语法:
" s7 G9 F' `0 _& rLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
0 d2 A1 Q) Y# G* T+ P# TUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表3 G! I& S( B) |( }% i
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
3 X( G; }: r9 o0 g4 N- ~9 C注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
5 N+ l( q- O' B3 d7 l文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
, a" j6 i2 t# Y& S0 C( q测试时,有个文件就行,叫什么名无所谓 总结:8 H- j: t; m2 i6 j9 f
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
* m. J3 _! ^9 _6 Z) m1. 高并发下单时,减库存量时要加锁
- @+ U/ s3 Y$ S8 C* J9 \2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*) A: P( C* D4 H2 c
- 模拟秒杀活动-- 商品100件' n& `( d" T6 U1 r+ G
- CREATE TABLE ta1 p" l: [% k( T1 @- y/ G
- (2 B3 i9 g; l! K- r+ g
- id int comment '模拟100件活动商品的数量'
$ H& \0 z% p* V. K8 U& J% A' J - );/ ?4 _' s& \! t* j2 x ]1 P/ o; W
- INSERT INTO ta VALUES(100);
6 w( |& ~. @+ M. E0 x - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件0 v1 a% I; q1 n# |
- */ 0 i- [ `/ W* d
- 6 w0 W+ b' l! n& y7 \7 X6 O
- // 关闭错误报告6 D6 w. G6 j6 M. L+ q/ ~
- error_reporting(0);
7 d0 o5 \2 X& b" T ], |" g
9 v- y7 _0 e5 \( U, K- $dbhost = 'localhost:3306'; // mysql服务器主机地址
9 y* B7 l; q2 T' u- t0 N+ K- _ - $dbuser = 'root'; // mysql用户名8 S) K& h/ c* |* f% N) o
- $dbpass = 'root'; // mysql用户名密码" P3 G0 _; h# b4 D7 Z( o2 j
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
/ c0 I+ I3 I" ]5 Q - if(! $conn )
9 L8 V! c, U; _0 V! L& d5 G - {
3 j" X8 o) l) H3 b. { - die('连接失败: ' . mysqli_error($conn));0 D0 \3 C4 Y9 `9 }4 W9 W- c; r
- }
# K [8 B0 `6 K9 l - // 设置编码,防止中文乱码
4 N6 s* j$ `4 c, @ - mysqli_query($conn , "set names utf8");
4 M, \- P6 {) q5 E1 |+ W2 l. g. _ - mysqli_select_db( $conn, 'temp' );
+ u" p+ E! F3 l - " V& Y1 |: {9 H0 T/ F4 A
- # mysql 锁
1 c( S6 J" V' I) K7 i - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 ; L* O* A& R$ \9 a* c G) W, Q7 ?
- $rs = mysqli_query($conn , 'SELECT id FROM a'); * c) l ]/ g4 a! L. Q7 o
- $id = mysqli_result($rs, 0, 0); 6 r5 ^4 V) M* Y6 S
- if($id > 0)
7 z, b8 ?4 K7 J4 c8 Y6 J: x - {
1 i: ?8 x7 T. o3 A8 r7 _ Y7 d - --$id; * E* y" R+ Q8 H4 Y& p9 B: N- y
- mysqli_query($conn , 'UPDATE a SET id='.$id); 5 v+ [/ f; ~( M1 H: u+ ]
- } / x7 @/ u& g& ^9 H/ B5 i" ]/ y
- 4 |8 A; Q: B, O. s7 P& w% w1 w' X# j9 J) N- D
- # mysql 解锁 / L9 J4 q E. W% m$ ]- k
- mysqli_query($conn , 'UNLOCK TABLES');1 f; @: @8 `# r* R
- //查询解锁后的id值
, [( [- }% r! {$ i* F - // $res = mysqli_query($conn , 'SELECT id FROM ta');
( ^) @ \* A7 t+ E+ \+ Z( J - // while($row = mysqli_fetch_assoc($res))
9 _3 }2 m. {' v2 S* G5 T - // {
/ ^$ A/ m$ i/ T - // $id = $row['id'];
* n, c4 k/ L( n# d/ x - // }
$ u1 Z* o) z4 Z) _ - // echo $id;
复制代码
: ^9 d8 ~ O; E( ~$ S9 Y# {& K* }( _4 f! _' |- x5 R( Q: l
e( z {* X7 G2 @" D$ [7 J* q
PHP文件锁示例: - /*; h, J9 W8 {# j
- 模拟秒杀活动-- 商品100件
6 g0 o3 g8 @5 y8 ?! ~ - CREATE TABLE ta; k% ^9 y" n% y. h
- (/ z. g+ j4 q3 y
- id int comment '模拟100件活动商品的数量'' B- {, H" D% i, d
- );$ C: A n/ n5 O( A( D: L) h* t# C
- INSERT INTO ta VALUES(100);
; m3 U* s0 v6 w. S - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件+ x$ {7 ~0 C4 ]. u* S& r
- */ 0 p1 Y% B' R- \+ D! T! D. S
- 9 N) e: v% d A: g' ]! `( Y
- // 关闭错误报告/ y8 L5 d1 a- a1 `; V! _. \
- error_reporting(0); . P4 G" M2 W; K' p# F
! p, V/ r2 f' N( v" `3 |$ @6 \- $dbhost = 'localhost:3306'; // mysql服务器主机地址, [) h. y- q% }7 G" Y4 }
- $dbuser = 'root'; // mysql用户名) p) }0 x3 c# \6 y' D4 Q) S
- $dbpass = 'root'; // mysql用户名密码
3 c& \. H, k |% K5 j9 B - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
+ Z2 p, [) P: ^3 _ - if(! $conn )2 C8 L4 A0 J% U. k2 A/ m3 H
- {
5 s$ k1 z% `1 H& a6 N$ Z& D$ ^ O! s - die('连接失败: ' . mysqli_error($conn)); R- v- a. G. a2 E- e" x1 F, U2 p
- }
4 g% g4 S" k' P7 Q( r - // 设置编码,防止中文乱码+ r, L$ l- Y. N. x- U
- mysqli_query($conn , "set names utf8");# k0 x' H5 U/ |) `6 e. y
- mysqli_select_db( $conn, 'temp' );
3 L- \/ R3 R& c8 I1 ^ - 6 q7 x0 ^; [7 O& Q$ N
- # php中的文件锁 7 g- t1 Z$ M' Y: W: z# ^; O
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
2 r. b3 o C8 N& a) | - flock($fp, LOCK_EX);// 排他锁
7 b& F, k: [! e6 u, }# y - 9 B3 C) M- P, w2 Z" ]1 D
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
# w G2 t: W& T7 O/ W - while($row = mysqli_fetch_assoc($retval))% H; L5 x5 ~! x5 r: c3 w1 z
- {1 D. k" j& {: q8 S1 e/ B
- $id = $row['id'];9 v& p! [* X3 ^; W, e. ?
- }! ?, ~$ k0 u6 b D$ @ \
- 6 w. s! B. I' i
- if($id > 0) % N% g* O9 I+ F" S* J) `# {
- {
3 P# P7 k$ {: @ - --$id; 0 J6 k( P( ]1 q3 Z( {
- mysqli_query($conn ,'UPDATE ta SET id='.$id); $ ]+ l: _8 ~6 _+ k1 A9 S
- } ) A- K! t) D! v0 n
- # php的文件锁,释放锁 6 n" L. G$ G7 J2 G- B; F
- flock($fp, LOCK_UN);
* q8 @7 ^+ B8 Q' G# e, A1 y, ? @/ ~ - fclose($fp);+ r2 K9 L* _1 P" D T
?' m/ _ j0 o" r' R3 s- // $res = mysqli_query($conn , 'SELECT id FROM ta');
; L2 c& K/ g( ?# i- _* C% G - // while($row = mysqli_fetch_assoc($res))9 K( e* \( A2 _, m
- // {
, `; }* W! p- w - // $id = $row['id'];0 f9 S' k/ t/ z" \
- // }# A, b' x1 a, v
- // echo $id;
复制代码 9 D9 [9 |4 d, L1 j
/ m# B# ^8 H- B _$ N8 {
抢券活动实例: - public function envelopeSnatching(){7 t6 K6 J% c; \" U% s, J
- $lingqu = $_POST['type'];& l* z* p. @) _5 R4 P+ _! N
- $uid=session('u_id');//用户id# Q9 q* h( S: ~* J8 q5 W
- if(!$uid){
3 l5 N# C f( A8 _3 V - $data['msg']='您没登录,请先登录!';# x' Z% A( u7 r5 u
- }else if(date('Y-m-d') != '2017-12-12'){% t4 H8 q4 f1 G& s6 S
- $data['msg']='不在活动时间内!';
, L8 W, |# p: P, i3 X6 E8 S$ `$ Q - }else{
3 s2 C: w) M& `* i8 _& A - $hours=date('H');//当前小时数& x& J1 i/ C: [- |2 m _
- if($hours > '09' || $hours > '17'){4 m) ^5 Q0 K6 j) G
- + M: F% Z6 Q ~1 f) _. B
- if($lingqu == 1 || $lingqu ==2){//点击10点的
2 q! [0 g0 t+ l3 D8 Y2 I - if($lingqu == 1){
9 I9 K& d) }# ?7 A0 `. N+ i - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过" u* j* x" _0 F1 j
- if($hours > '09'){9 A8 P. t2 }. t3 C, T1 K2 w
- $num=mt_rand(25,28);//优惠券金额 R( y0 ]3 q, `% p8 L
- $id=1;
* D1 K7 E( P( x1 l - } Z/ t# O9 k, U M! M& p: ]
- }else if($lingqu == 2){/ I5 z3 } m$ q6 J3 c8 [- j
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
* Q" S* a; ]* Y j - if($hours > '17'){
* E! Z- Q! h9 k - $num=mt_rand(50,55);//优惠券金额
3 n- H6 P: k% f: [ - $id=2;6 A+ I/ [! _4 A0 |- I+ b
- }
0 J$ B" V8 ]3 t, p4 l6 N - }
! e7 H4 ]6 G% {1 k% ^1 ]5 i6 w - if(!$id){
t! [ [' b! e - $data['msg']='时间还没到,晚点再来吧。';
% u& Q+ Z$ K# E% L5 B' R - }else{# l( g2 r$ ^# b( P' X
- if($is_lingqu){. t' i; z4 I% u9 h6 G6 O' |! R
- $data['msg']='你已经领取过了,留点给别人吧!';( Z U3 B6 J) h7 r3 T
- }else{2 T( A+ [4 `3 M% h' S% W" G6 U
- //锁表1 V8 A" D) Z8 V: h! z
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
! p% f1 M; V4 n - $active=M('active_num')->where(array('id'=>$id))->find();
^8 s8 [% W; S4 J - if($active > 0){4 }7 ?2 O( S% [0 \
- //开启事务/ q( u" ^5 L% q/ O' e ]( i! F
- M()->execute('start transaction');7 T; R" }+ {+ \8 W+ ^* b p! y
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));* G- U) Z6 B1 v" }4 R. n! x. J
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
, g2 D* n& R' M: j$ u7 _# j, G - $members_preferential = M('members_preferential');
9 d) y4 V* u2 y9 g8 x) P7 |/ V - //对应投资金额,! d/ g- M; p, N( n
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
" l! ?% _' {- ` - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');/ ?: r; T* P1 f& Z/ C
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间) x6 m4 a: E& U/ ]* u: t& R
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
) `- `" d/ B! V3 _8 F% d1 w) I - if($save && $add && $add2){
) u8 N( ]' t3 a5 u% N7 x - //事务提交1 \6 p* F5 c" K
- M()->execute('commit');
) D+ L$ d2 R: E - $data['msg']='恭喜你领取了'.$num.'元优惠券';
+ Q2 i5 j: c# O% q* F, V( ?7 A% n - }else{% k1 w( D; q7 Q2 W. K S
- //回滚
8 E& W6 K- P) c7 S0 d/ ? - M()->execute('rollback');
1 z4 v5 B! p2 R9 k1 v - $data['msg']='未知错误!';( M4 n- ^) u6 `& }$ Q
- }
# V0 E* z* O; t5 K; ~ - }else{3 W2 }$ x0 O' p. U, k; N
- $data['msg']='红包已领完,你来晚了!';9 V- W3 D! D7 k) f/ G( |/ Q9 b
- }
5 W# O p) Z7 U1 v - M()->execute('UNLOCK TABLES');$ \ \: O6 Z& S" l& |; ^
- }
2 n9 W" t) v5 i/ k - }" u# K: a" Y5 ~3 k5 Y, Y
- }else{3 b, g9 z$ N/ o5 M
- $data['msg']='非法操作!';0 _% Q( Z6 |/ i( X8 g: F( W
- }( G R! k/ Y1 j: J
- }else{
& G# u( E2 k9 b# D9 p' H: P' K - $data['msg']='还没有到活动时间,请晚点再来哟!!';
% [( \6 h1 Y, E - }, i( |$ [: [, S# v( l, b
- }: \ x: K1 s- N) B) U' Q
- exit(json_encode($data));
8 t' k) |1 O8 A - }
复制代码 . f9 @( |, B) U" p: ~3 x
0 u7 ]: q) T' {1 I/ h, { |