|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 ) A# `+ l( ^* K$ E( n2 F
Mysql中的锁语法:( j( U# @6 Y P! E' }6 `
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
1 ]1 L! p9 E3 ]# z {9 P) T, iUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
4 b% n! P) c- N0 p* t* w, B" sWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
$ b) M6 I$ u+ J: Q/ _9 @4 c注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :) W3 a5 f% w4 W8 k
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。, A- Y4 O% G. L6 S+ t
测试时,有个文件就行,叫什么名无所谓 总结:
0 E4 y. k, L/ y/ E项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:! c4 K+ X: E) c/ ?! t7 q, j* H
1. 高并发下单时,减库存量时要加锁
, R! [$ q- v3 b `8 ?2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*, X5 t# j: R! s- D; |# j/ M! x
- 模拟秒杀活动-- 商品100件
& C- ]7 y( y; a4 u - CREATE TABLE ta
* l, F5 S1 y6 b% ~# l$ g! e - (
9 T7 x: {5 B, w# ]! i) e( o! m5 Q - id int comment '模拟100件活动商品的数量'6 X& f* n8 M* Q% g0 w" ^
- );
/ x! A8 B& S. E/ P; Y- s: x! c2 h - INSERT INTO ta VALUES(100);
) b& W9 N5 d. f; o- b - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件9 Y- J1 t7 m7 C* x
- */
, k& x ?* q0 b8 p4 _" h - ) M+ r! A4 I' }) E1 N5 Z( s
- // 关闭错误报告
0 P3 }" x- e5 B2 s - error_reporting(0);
& W# h; g @$ c$ M: ?
: Z7 `1 e2 c) O/ q. M+ z, J3 e- $dbhost = 'localhost:3306'; // mysql服务器主机地址. W2 P) o: }% \% }4 @3 l( e
- $dbuser = 'root'; // mysql用户名& n% d2 R0 V) L, ]) D. H
- $dbpass = 'root'; // mysql用户名密码5 c) s2 P* c8 j$ c! o
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
) b# j. B9 z. x; `5 l9 [* C1 t - if(! $conn ): Y1 A! m+ y4 ]+ e$ W
- {
# F8 J0 u4 A7 z- @% J( m - die('连接失败: ' . mysqli_error($conn));& R$ O0 v$ R' A6 M+ z) b- C
- }
. i) O6 b/ {% x" ~( F' c! q8 k* N - // 设置编码,防止中文乱码
; U& F+ [& W1 d& r# h - mysqli_query($conn , "set names utf8");1 W( O0 x" R2 f o
- mysqli_select_db( $conn, 'temp' ); , y; r: x+ _( p5 C9 K& h! i
4 k" R+ U" y* W) q2 E# X# }- # mysql 锁
7 x6 A0 _% n! G2 I: ?. o; Q" w* h/ @ - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 q3 J: E) U* @% k& M$ q
- $rs = mysqli_query($conn , 'SELECT id FROM a'); & O$ b! J( M g. Z9 u" ~4 g, F& `
- $id = mysqli_result($rs, 0, 0); $ o M% d1 n5 e9 @% |
- if($id > 0) 4 u2 M3 _7 m8 k4 }9 `
- {
" e6 a& b2 @* R" e! t - --$id;
( R2 X0 p( n& k+ S - mysqli_query($conn , 'UPDATE a SET id='.$id); 3 {4 _8 h! v; Z% s( B
- }
. u/ I( @, I [# i& {# P. @- [ - C) Y- s: s3 ~2 e4 l
- # mysql 解锁
( p' k' H1 I) ^/ U& a7 E, F3 X& @% Q - mysqli_query($conn , 'UNLOCK TABLES');8 t/ C6 ]+ w& \+ `) H P' E
- //查询解锁后的id值1 `3 o% H9 f9 {4 w
- // $res = mysqli_query($conn , 'SELECT id FROM ta');5 _4 y, {" ~* p, [' i- A
- // while($row = mysqli_fetch_assoc($res))4 Q' u4 f O. t- s( p/ h
- // {
, f! T1 y r- k* M9 v; N9 k$ F - // $id = $row['id'];8 K) |% ~5 M2 s& d
- // }
9 G0 c1 ?+ k8 `! C x% m - // echo $id;
复制代码
, m4 L# q: k. I& v( k! A* N+ ]
. v; r- n/ o v5 I3 p5 c5 z. @$ h; G" M
PHP文件锁示例: - /*; J3 U4 ]1 [. m6 a/ N
- 模拟秒杀活动-- 商品100件/ W) X2 S' ~ o5 G- K# b) d
- CREATE TABLE ta+ h3 C4 r/ }2 V0 Y+ _+ Q
- ($ V! r! J! ~7 k7 d4 t& R
- id int comment '模拟100件活动商品的数量'3 E" m$ y! F# J2 m
- );
7 x8 c: Y0 z: c, ~ - INSERT INTO ta VALUES(100);
) O: n6 G; }$ \8 K. t8 }% l - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件: P3 G: O! V5 a6 p1 l
- */
7 T: b3 L: n3 U& Q3 q3 ~, ? - ( M9 y {# W0 e9 i& K
- // 关闭错误报告/ |. O% |8 q2 V& H" j6 m' K/ X
- error_reporting(0); 4 y# t0 F; |3 O! l0 A
- 6 W" U6 k, q) D* m
- $dbhost = 'localhost:3306'; // mysql服务器主机地址, k J: N Y3 u6 R5 r* A
- $dbuser = 'root'; // mysql用户名1 H- v. W) ?4 s8 U
- $dbpass = 'root'; // mysql用户名密码. v/ o8 b4 U( m/ x
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);4 s9 `3 T* }% J, T" C
- if(! $conn )
1 r7 B& F4 ~9 @, F - {
1 [! S) A: n9 @7 [% h7 w - die('连接失败: ' . mysqli_error($conn));
& D0 Q; s9 p( \. U6 E4 _+ a2 H - }
* h/ b9 H. t0 q+ [ - // 设置编码,防止中文乱码. D9 K2 c' o; ~: x5 u) r
- mysqli_query($conn , "set names utf8");6 q. r5 D4 H7 H" Y* Q2 F
- mysqli_select_db( $conn, 'temp' ); : ~# [. S. Q- O4 C2 p5 X
; j% H f: E) {! Y, i+ M: n% u- # php中的文件锁
, s: J8 w' ~1 T1 o8 o0 s - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
) ?+ u1 N0 F2 ~1 ` - flock($fp, LOCK_EX);// 排他锁 9 ^4 d6 @# ~1 ^* { U2 v
- ) U% S$ @, h% |& Q# v* r
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
W3 V2 @, k* b& l - while($row = mysqli_fetch_assoc($retval))- q" V' w9 P* D2 ^" O& H
- {
) r3 W) _: u( O# F+ p - $id = $row['id'];' m' |+ M" J& V( G9 Y! {+ A! ~
- }
4 k0 e/ K1 K& _+ l( r -
% \; R1 {: |3 G; z2 t9 E% S2 s+ Y - if($id > 0) ; c) p; M' q/ I! O
- { $ Y& r2 u$ C7 M9 h! ]! w
- --$id; * I# Y/ K1 O" A M/ H3 c! U
- mysqli_query($conn ,'UPDATE ta SET id='.$id); / D! q7 l* X# y7 \) n
- }
, e5 [$ M( q; v - # php的文件锁,释放锁 $ _ b0 q/ q+ S. g( x
- flock($fp, LOCK_UN);
$ }; ]: m8 w' o2 k - fclose($fp);- B* [1 b; C7 L$ ]0 T! W
- 4 J* l$ O( w* ]6 I2 \
- // $res = mysqli_query($conn , 'SELECT id FROM ta');0 g, E0 } A; d+ J, B3 |
- // while($row = mysqli_fetch_assoc($res))# ^2 Y, R" h5 ?7 J
- // {
6 x6 S- s7 F7 n: d - // $id = $row['id'];6 G( O5 u @$ M6 F- a9 O2 d/ n+ L! c; _
- // }, A; t$ Y4 r) m5 Q b
- // echo $id;
复制代码 & m7 Z. {) `- o; d. j3 R8 f* f, a
+ O2 [3 `6 ]5 E1 r4 A$ o
抢券活动实例: - public function envelopeSnatching(){: R! z: H Y; v5 O- ^( O
- $lingqu = $_POST['type'];
! J# d3 E2 o$ k! ]& l - $uid=session('u_id');//用户id D' Y, R& i4 y8 I X
- if(!$uid){; z' k0 {; Z& ]9 O
- $data['msg']='您没登录,请先登录!';$ C( c0 T( e8 w4 Z" X+ F+ o7 F
- }else if(date('Y-m-d') != '2017-12-12'){
; W2 ^6 k" Q: p& e% ?& B8 r - $data['msg']='不在活动时间内!';
. x; h- b! y$ g8 j5 k; C - }else{
0 z9 j/ S7 b7 B9 U9 Q5 t5 ~ - $hours=date('H');//当前小时数
: ?% \- R. F* V+ d - if($hours > '09' || $hours > '17'){
' i5 p w( i2 [7 j% v2 r - 7 j0 o' g; c4 \+ X9 J
- if($lingqu == 1 || $lingqu ==2){//点击10点的
9 v# B2 {( z+ P - if($lingqu == 1){; F! c& ]0 X$ `# i
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
+ _2 s" Y5 r. {2 C- J/ E - if($hours > '09'){3 G7 p8 \6 c' B; S' H: T
- $num=mt_rand(25,28);//优惠券金额/ X/ x/ [1 G* E
- $id=1;
5 g0 T& O; C" _, a4 Q2 v9 ] - }/ {% S' a. u/ u! {4 E/ o7 ^
- }else if($lingqu == 2){
0 E* o6 y- f/ w" ] - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();+ l* S$ v. O" S: R2 J7 O+ z5 {
- if($hours > '17'){3 g4 F& Z: M% ]5 T* A
- $num=mt_rand(50,55);//优惠券金额' \" d/ e- V7 \! v1 m$ k
- $id=2;
. e& w3 v9 M, e2 p - }( p( i" a( O% o
- }
6 @+ c4 w% p3 e* n m& c: k, T% V - if(!$id){
' ~1 h2 @$ }# }+ q5 M# Q7 }+ N1 R - $data['msg']='时间还没到,晚点再来吧。';
1 K+ E5 ?: M, Y0 A& d. W - }else{
3 r, m# V* V3 N3 K& _$ S2 d - if($is_lingqu){
1 u1 h3 X( v% t2 b - $data['msg']='你已经领取过了,留点给别人吧!';
0 I& ? L S0 v# o6 z ? - }else{
3 O- {8 c' g+ a - //锁表" N- X; J" u( w) n& k
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
5 g9 t) V* J/ j - $active=M('active_num')->where(array('id'=>$id))->find();8 u! O$ t7 E! t @& m$ X
- if($active > 0){
H5 A m: Q& h2 |0 y$ p - //开启事务
; _* D I# M- B! y: u6 x* C0 Y; N - M()->execute('start transaction');
) H8 t. h$ N& h7 Q) Y0 k7 p6 J - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
, }, l" ]* @! d - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));8 h+ I7 U7 G7 \8 X, U
- $members_preferential = M('members_preferential');
+ j i. g5 j2 y& ` - //对应投资金额,
8 q* t6 ]( M; Y3 i( m - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
; m$ W. c0 F9 T; g8 X( n - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');. R5 _8 h# M2 v
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
/ a4 j7 M0 W5 H# v/ Y: k% p - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券4 D/ q, n# u% m+ |3 i
- if($save && $add && $add2){
( r' {* q1 @- i7 n# v) j& e2 e - //事务提交! H ~+ G9 Q: w5 d% h
- M()->execute('commit');* d; l* `, R# o6 _) W
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
6 n- a) r- l7 r( a; b% ~* ? - }else{+ g' q5 h3 ?, W! ?+ v# ] S1 y
- //回滚0 d1 B- v; {5 _- M& ~ W4 F. p" n
- M()->execute('rollback');
1 P' f6 j; {7 K9 m - $data['msg']='未知错误!';9 E( _4 R( \4 M7 H
- }
% |3 s5 ^0 }' H. K' z |7 n" [6 [( \- G - }else{
! G# X7 ]2 p0 t8 n0 g8 n) N' J+ _ - $data['msg']='红包已领完,你来晚了!';/ g7 ~( w8 x* C
- }
+ F8 g1 W7 r) z; ~+ U - M()->execute('UNLOCK TABLES');
" `3 m/ _% d8 A1 M" b- l# P% ~- K - }1 Y7 g) _' l1 {+ u
- }
& i3 w" o8 s2 I9 i. u - }else{
0 N- \! K* t5 ?9 G8 J! c" t - $data['msg']='非法操作!';
8 c6 z+ V8 E' t$ U3 N O" L - }
% B% ]7 Z" _( R& U - }else{
* o, y" ]) K: M# Z - $data['msg']='还没有到活动时间,请晚点再来哟!!';
& Q% W0 B2 W3 z4 K8 }/ N s - }3 @' T2 @7 _/ ]# N+ K
- }: A9 b9 J/ L8 ]. Q# l
- exit(json_encode($data));- C) Y' M6 `2 r3 n
- }
复制代码
: h' ^9 z6 S. Y& S+ P! N7 _: c+ o2 }+ Q* Q! H
|