模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 + ? M0 G: b5 D1 K' t6 O+ \
Mysql中的锁语法:5 z" J6 i; [3 ^* o+ j# ~! _' p
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】: C2 ~- h1 y9 | ]# F7 s/ Q
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
2 X% C. `' T# i8 \Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞6 E8 U b: _) c" X8 ~% a
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :- p7 t! ?( A% X- L4 U, p' P5 P
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。* Y# b! `+ t" r, b
测试时,有个文件就行,叫什么名无所谓 总结:2 _# D% M* z% n) c# t' k
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:4 q) x4 d+ M6 Z7 Z1 A9 t" ?# B
1. 高并发下单时,减库存量时要加锁
4 o5 S2 t/ O! d" S1 ]( k, h4 K2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
$ j) p8 U7 |, N - 模拟秒杀活动-- 商品100件
8 U. _, j, L+ A- O - CREATE TABLE ta
9 J, L) h( M# y0 Q- t, K E7 b - (6 B4 ~* A4 J2 q* G
- id int comment '模拟100件活动商品的数量'
W5 I% @0 N. g F1 p - );
: t$ ]& ^3 e# v% e k3 J- W) c - INSERT INTO ta VALUES(100);1 U* z8 E5 C1 s9 H
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件) r; O. ~/ B# h. i/ q
- */
0 Q% \% t, V% P; u) n9 ^( } -
- Z4 D/ k% ?0 t" Z. K/ e - // 关闭错误报告
" A/ U9 {$ g3 @8 h( \8 V - error_reporting(0); " s% g! q; g3 x4 g
- + D! }( {9 h" _/ v- k
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
- s8 M' I6 x. \ z7 U5 @4 A4 r - $dbuser = 'root'; // mysql用户名
# u$ Y/ i. G0 a$ I) z; R: v. m& A4 Z - $dbpass = 'root'; // mysql用户名密码
2 F6 z# F0 {7 P% L4 j% v* H& O - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
: M, `* l( { I4 b* u - if(! $conn )* G$ K0 a) _1 Z4 T5 \1 |4 q9 D2 m
- {
1 G4 F8 w" V" l% `$ k5 U5 F% f) ] - die('连接失败: ' . mysqli_error($conn));9 X/ b7 e4 s/ I0 T
- }
( N+ f" _; T l - // 设置编码,防止中文乱码% z2 {: I: o3 |( t! z: J
- mysqli_query($conn , "set names utf8");
7 p, h% u1 ]; z4 T2 h- M! E - mysqli_select_db( $conn, 'temp' ); & T9 m }: P3 G8 Q# Y
+ ?, o6 [( {+ i% A- # mysql 锁 1 v& x1 t% y/ m# o- \0 Q
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
1 J. x8 H7 w7 @) w2 Y - $rs = mysqli_query($conn , 'SELECT id FROM a');
& L% V# L$ e, e6 }1 c - $id = mysqli_result($rs, 0, 0);
9 S. h* i% M/ o* | - if($id > 0) 1 e, H* E. H s" g! t$ r
- { 9 E9 y; v0 e! A1 v) {
- --$id;
# r4 ~' Y3 O3 p6 G4 c$ U& Q - mysqli_query($conn , 'UPDATE a SET id='.$id);
1 r. V% [. F9 v6 [# v( \% U: i - } 7 s$ `5 x1 r1 K8 R6 e- F$ y
- " A$ I) U5 |6 m6 o
- # mysql 解锁
! I; V! \. ]8 r# U# _: \0 v/ _ - mysqli_query($conn , 'UNLOCK TABLES');( ~. F! Y7 Z/ x: G* _. A" S
- //查询解锁后的id值
# Z: r7 N: u' f' b6 {; }/ I z - // $res = mysqli_query($conn , 'SELECT id FROM ta');7 D: z; E; v- L- N. P) _) e( D/ u
- // while($row = mysqli_fetch_assoc($res))
) _; S( Y+ q1 _. f' O7 L - // {
# K, F8 l* ^; p! m% R3 h8 {2 A - // $id = $row['id'];
2 |. w, C' @: e: F- b: | - // }
1 ^% `" q4 v! n% D9 I5 X7 A - // echo $id;
复制代码
; ?! D/ b% s& j% c$ n5 \0 U, K1 Z# [2 d2 R9 l; u2 ]% }$ H, _) v/ F
4 b { O0 |- ], l2 ]2 ?9 U% G
PHP文件锁示例: - /*
: K/ P. R& o6 }- u - 模拟秒杀活动-- 商品100件) \; o% C6 v* Y3 Z
- CREATE TABLE ta' x/ T, b0 b" t$ H, ]
- (# Z, F2 i$ E+ O! F% t
- id int comment '模拟100件活动商品的数量'
* G4 S t3 G# G( S7 K4 O$ _ - );
0 m6 E3 L! C/ V" f6 X3 M% F - INSERT INTO ta VALUES(100);
$ q: a% Q% w1 u, i S! i! X - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
- c" P- [* y& Y$ c4 K2 G - */ 8 h. N8 L/ d& n
- 9 Q6 c& n' d8 A5 {% ]* z
- // 关闭错误报告* u0 a7 l, A& }) F4 Q( m- D: _
- error_reporting(0);
7 N2 G, c$ e0 Q0 W4 U, B; ~ - - x; [2 E! D% G! P9 k5 p- T. N
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
6 n- W( j% W. r- b; ^! Y2 Z - $dbuser = 'root'; // mysql用户名
. ]* i5 w# F- Z; Z- | - $dbpass = 'root'; // mysql用户名密码
, }6 R' ]% W* U# r4 ^5 D# F5 I* b - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
5 N6 A+ R3 x7 H( ? - if(! $conn )9 `5 r# M6 A( T7 T+ t% B. b
- {
, {9 S" o+ b% }( m( A8 U - die('连接失败: ' . mysqli_error($conn));
t- _0 e0 D$ F( a6 K - }. m0 j$ K7 P$ j" j5 @
- // 设置编码,防止中文乱码3 y0 d5 [2 q: A0 C8 O$ r$ G8 ~4 O: X
- mysqli_query($conn , "set names utf8"); a9 M" [1 ^8 ~7 A5 M
- mysqli_select_db( $conn, 'temp' );
6 H! y* U; C) l& @
9 l2 K- V3 l& w9 [" {) t- # php中的文件锁
' b3 s R: S! `' r+ a" T# z( \2 o - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
! l! P2 r0 a1 ] T8 R$ s - flock($fp, LOCK_EX);// 排他锁 : e( Y& `' r! V# V
- & B5 B; Y: g8 ]+ ~* a. n- l9 l
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
/ E- l. q! `% N$ Z+ u - while($row = mysqli_fetch_assoc($retval))
# S: j8 H- F1 V2 Q% q, h - {" P9 j7 k6 |) @8 b
- $id = $row['id'];- z4 \# ~+ \* K1 q
- }
# m* a/ C5 ]4 L9 }5 N, w - & B+ x. N8 Y# K( V" ]* w+ C- n
- if($id > 0)
. X, n+ j' M8 z2 c7 {' z - { 2 x" z* M- K; ]/ d2 d& m
- --$id;
) ?5 E }) y `9 ^, D9 F - mysqli_query($conn ,'UPDATE ta SET id='.$id); & U- F2 [# O; @; T& j! O
- } % ~0 T* _6 C6 ^6 Q& @4 n
- # php的文件锁,释放锁
8 r' F2 M! _) _& I3 Z3 F6 N0 F - flock($fp, LOCK_UN); . \! s1 c& r% m5 q4 w7 L
- fclose($fp);( O0 I! h9 D5 }1 {3 e0 i
- ! z0 A2 Z+ x6 Y' H
- // $res = mysqli_query($conn , 'SELECT id FROM ta');" i" `$ b# f" J' l
- // while($row = mysqli_fetch_assoc($res))1 t+ B" ~5 e; ?% ?+ Q" ~. H
- // {8 K1 ]7 ~6 h4 R7 N6 y
- // $id = $row['id'];
+ {4 {$ y. ^, l* l2 \8 o: A( K. ^% q! d - // }
. S' g7 I! y! V! a3 ^2 k( E# I- P - // echo $id;
复制代码 3 n' }/ v# I9 u# c) C) Q+ o
5 V7 P- M. c1 N$ z" m6 o
抢券活动实例: - public function envelopeSnatching(){
5 [5 S8 d. Q5 x' } - $lingqu = $_POST['type'];! B' ~; v1 h; J& g
- $uid=session('u_id');//用户id
; D' o9 w+ @5 ` H1 j) s! R1 m - if(!$uid){
# n, s+ Q8 E4 }9 X7 v$ P) U# b% f* f - $data['msg']='您没登录,请先登录!';* e: y& b$ z# _* w
- }else if(date('Y-m-d') != '2017-12-12'){# Q3 H9 c6 ]( X6 V" {2 _- ?7 \- P
- $data['msg']='不在活动时间内!';
+ c6 Q; W9 v* c6 q, r - }else{: a7 F _! ~* W, k4 c# S
- $hours=date('H');//当前小时数
8 i$ Z% }! V7 l/ k0 f1 A0 f - if($hours > '09' || $hours > '17'){
( M8 \& m' @& N) p$ Z
% I! n2 R1 G% q1 U; ^- e$ N- [; R1 u% p7 O- if($lingqu == 1 || $lingqu ==2){//点击10点的
$ o) _$ _ p1 ]. x/ v0 H - if($lingqu == 1){% _" L% y0 w. E
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
: ~8 ]% w' Q& w' G7 q - if($hours > '09'){7 r4 l4 J7 ]( U3 Q1 S0 d! s$ ^
- $num=mt_rand(25,28);//优惠券金额
6 \9 x4 o( y4 y; @: {/ Z - $id=1;
1 _/ N/ p4 K7 @) K - }
; N6 ^" k3 P2 _4 u/ J9 g - }else if($lingqu == 2){
/ x" r8 }1 G' w/ P9 D! B& n7 ]; o - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();2 N" p/ S7 M% k7 H+ R
- if($hours > '17'){
- P$ _8 {: J+ h1 i* j( |! Q9 k - $num=mt_rand(50,55);//优惠券金额
: W, H, g+ o ~( m+ W4 @4 k - $id=2;
, g6 I x7 V' z* | |" t9 K - }
" t6 N2 X: x% D - } y' b" y! r& _0 _6 O- z3 ]
- if(!$id){) H4 Q4 B/ [0 h* n
- $data['msg']='时间还没到,晚点再来吧。';0 y& ?* v0 S+ E* f' o- D: H( f
- }else{- F' R: Y$ u" O! F6 }7 s! V
- if($is_lingqu){% c0 ?8 h W Z' w
- $data['msg']='你已经领取过了,留点给别人吧!';
" e }# P/ x \) X/ }; q' e - }else{
" J" W9 F3 N& g! w# i# A( Q - //锁表' a' ], g F; ]5 e; U
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');5 Y& @ Q4 B% q
- $active=M('active_num')->where(array('id'=>$id))->find();* O- E6 Z/ g7 N* e
- if($active > 0){
, |6 y+ _' s0 ~( c0 X - //开启事务' f% R! H8 w: ~; L. w
- M()->execute('start transaction');5 S9 v: J! W6 W3 I
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));- e8 c, e2 ]7 J( y
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
+ j' p$ T1 z! v* ^ - $members_preferential = M('members_preferential'); i& p# t% Q, H) E1 h) a# R/ C
- //对应投资金额,( d. }. D4 x' h7 l) W
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
; T1 f9 j* a5 H& ~, F @1 e) ~ - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');1 k$ o% [$ v* G6 o8 r' S; s5 U7 l$ x
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间. g8 @; ]5 `: {& c/ b
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
5 k! j$ S! N% K9 B - if($save && $add && $add2){+ F8 r% a, L% s* E3 S
- //事务提交5 V% @% k( c" v0 @3 o+ [) ?
- M()->execute('commit');
+ Z0 a& G' V4 ^ - $data['msg']='恭喜你领取了'.$num.'元优惠券';
6 I& q" p) C ]) Y: n. O3 E - }else{* u) K4 d ~7 H U
- //回滚
5 x3 X1 X- @3 Q- x1 U, i6 c - M()->execute('rollback');! @& k/ n# R+ H) w& N$ I+ q
- $data['msg']='未知错误!';
- ?7 \7 m3 z9 D" Q% i0 | - }
- r: \6 D, [ i# N U1 J4 T - }else{
$ K6 {. K% O. A$ i - $data['msg']='红包已领完,你来晚了!';
' C# f: @; N/ x - }
! O! y8 s5 P" h2 w+ b$ M w9 p - M()->execute('UNLOCK TABLES');" J0 K; Z) Q- ~+ B' i0 e
- }
# t7 R) J1 K( B# { - }7 `& ^( E9 I- L9 \0 v4 L, D0 }' d
- }else{* z* U$ ^( ^7 x
- $data['msg']='非法操作!';3 P9 p o, S8 a# n! y9 v
- }% Y @. f! r+ F/ m! @3 T
- }else{3 C$ g% m! T5 B0 n7 H$ ~
- $data['msg']='还没有到活动时间,请晚点再来哟!!';6 w, a/ U! u) D& q8 r
- }, I1 }1 Y& J) v' ]& E: g
- }& S# C, r7 }% @; ^
- exit(json_encode($data));
2 N) w# T- e3 W9 ~- e' b6 U - }
复制代码 ! F% T- Z- M( U/ S7 ^8 g/ P5 `, S
. H/ P. ?9 J5 E |