|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 " S# v$ e. z% |3 e
Mysql中的锁语法:
7 J( |$ C2 b- G( x+ X) FLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
' }2 A4 Z4 t9 F8 I1 Y. @UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
2 p% o% ?2 @6 r( H6 x1 N! @3 V0 PWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
! }% y8 _. Q0 C2 X. e/ ]注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
( V. ]8 B6 d2 r) `7 P文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。# u" F/ l- l4 X5 H2 I' p# q
测试时,有个文件就行,叫什么名无所谓 总结:) B( M& L/ ?1 k9 t- i
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
, s5 }# t" S3 {4 z8 V* G1. 高并发下单时,减库存量时要加锁
* x6 V h6 [3 y& ?* @5 G2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
8 w1 c# I/ f' Z$ X. T - 模拟秒杀活动-- 商品100件
* x5 z2 Q! V4 j* Q - CREATE TABLE ta
' i9 l' }2 [' h: ^2 N7 P - (
$ T0 K m. p2 m% D: C8 u - id int comment '模拟100件活动商品的数量'
3 T' N' J9 m7 s0 _' y6 W - );% T' c [; k1 K4 S+ c3 x/ ?
- INSERT INTO ta VALUES(100);
* _8 w: f! { l$ u+ i; O( f0 i - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件* N' L) W; H$ C/ c9 Y* V8 }
- */ 9 B+ d! D$ x4 \2 C$ t. m$ J
-
9 N# M; ]0 E$ w) c( G - // 关闭错误报告
4 C. X; z+ z5 k: I# Y$ |* k - error_reporting(0); 7 ]" X; c& ^8 t6 Z0 N" N5 ?" ?. G: s
0 `6 N3 }2 {1 C1 {8 ]- $dbhost = 'localhost:3306'; // mysql服务器主机地址
# f) H6 `. s4 X% J3 U5 T - $dbuser = 'root'; // mysql用户名
% a! Z: T" Y& g1 c! H8 f - $dbpass = 'root'; // mysql用户名密码4 Q+ Z- h8 x* Q4 g8 p9 d
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
1 o( w/ n, ?+ Z& I/ C8 N3 Q0 R - if(! $conn )0 U+ o9 W. L; l) k% {/ B/ D) D
- {) S1 I1 X0 R# e
- die('连接失败: ' . mysqli_error($conn));
, T$ S, F( j, j N8 X, Z - }5 R* D4 j1 c" k2 u& `9 @; ]2 ^: Q
- // 设置编码,防止中文乱码
4 i3 b$ E- w4 F' \7 k" Z - mysqli_query($conn , "set names utf8");7 f4 h* `& e' b2 C- U* ?/ m
- mysqli_select_db( $conn, 'temp' ); 3 {1 [* C. w9 x6 J/ p/ ]. o# K
8 ^$ c4 G- H8 t- # mysql 锁 , ~) g8 |! B! T5 D6 L
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 8 ?' F8 l* h+ P- u
- $rs = mysqli_query($conn , 'SELECT id FROM a'); . q' N8 F$ @2 b, j
- $id = mysqli_result($rs, 0, 0);
% U2 `: @' G" H3 ?) t2 C2 y* F - if($id > 0)
' H3 F4 D9 V7 T! e, j/ C) U - { 4 k( z) [3 i0 ^& V2 \; f
- --$id;
2 S0 H, Q2 o6 `" ] - mysqli_query($conn , 'UPDATE a SET id='.$id); # L( t' t# }: @
- } 3 \& @9 [( l8 \" Z) D
- 5 m* C, n. J$ P% C! l
- # mysql 解锁
% T; ~0 X$ S+ c; d, x - mysqli_query($conn , 'UNLOCK TABLES');$ V7 }" q( \5 |# X
- //查询解锁后的id值6 ?0 Z4 g( j; m! v' J. H: x
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
]3 _. b# [/ w - // while($row = mysqli_fetch_assoc($res))% r1 | H6 x$ Y5 u$ k5 L
- // {
: ]. A7 |2 x" g - // $id = $row['id'];0 R4 ~8 L1 ~5 b7 n2 N$ W( d/ I
- // }
# G" q) ^2 W% O! R4 E+ c - // echo $id;
复制代码 5 t0 D* B6 z) B5 ~- A- T+ e
( i' A( h7 ^: Q4 ]! A
. B$ T3 _# A" m, V3 H0 i0 S5 yPHP文件锁示例: - /*# L: y* L) r1 g7 P" B; Y! X7 j7 {* F
- 模拟秒杀活动-- 商品100件
& V2 l9 ^; b+ y- p+ _& s3 ~9 J - CREATE TABLE ta
( r' T8 i% s! r- u - (/ O$ q% ~0 `4 F, v7 ]& j( r8 o
- id int comment '模拟100件活动商品的数量', T2 p; k% h% u
- );4 c! s& y; H2 S) \5 [* A z5 K' F: U
- INSERT INTO ta VALUES(100);
/ Y& I0 B% `, H2 H$ ?1 {% p - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件+ _8 b R, x6 w
- */
2 Z4 w* j- _( i% C# w- n9 ] - , x7 O5 K/ i" ? ]
- // 关闭错误报告
# | d, g& d; a, b - error_reporting(0);
, x4 w( {9 _7 d! c3 J7 v4 O6 t
$ U, m. q1 Q2 I- $dbhost = 'localhost:3306'; // mysql服务器主机地址8 j! U1 \; u1 y$ e/ K' Y0 I
- $dbuser = 'root'; // mysql用户名
0 t& ]3 L* I% w0 x; z! a( s - $dbpass = 'root'; // mysql用户名密码
/ d% R! ^! @$ b. @ - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);- t( ~$ x. z+ d7 `9 K0 X
- if(! $conn )
8 m; p& _5 ]# I - {9 J% v' Q- A, L! H2 ?
- die('连接失败: ' . mysqli_error($conn));5 u8 Z+ ~* L" D0 E/ c% E% }$ Y* I4 B! F
- }
) j) p: S" _: Q) { - // 设置编码,防止中文乱码) W5 Q$ K% ?1 ]* |# \+ i
- mysqli_query($conn , "set names utf8");
2 C! s& g# i7 j5 s - mysqli_select_db( $conn, 'temp' );
5 J7 E3 N6 `/ H8 S; e y! d8 d
" i! |) }& o# w0 g3 a0 [- # php中的文件锁
! c; g1 V. Y$ U9 j6 t. E - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 3 r! Y$ O; W! }6 c) s! I
- flock($fp, LOCK_EX);// 排他锁 ' `8 |- |* X* x( X. m
- 8 h; m3 R- a) i8 L" j7 m; \+ R* x( y
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
2 N2 U/ H0 z+ v& h0 G ~ - while($row = mysqli_fetch_assoc($retval)); V1 _. ~; z6 B+ A
- {
. Z; S4 a& ? `( _ - $id = $row['id'];
! N3 h1 x H- A; S3 @7 L" A+ o- l - }
. A- _9 X1 b! J. M% W8 ] - ) U. z9 c$ P- M: \$ y3 E1 ]2 c( w
- if($id > 0)
4 J: b* H8 \& y; ?) f; d - { & y% j ~( [0 g
- --$id;
. c+ b9 o) F; ]! b; |; Z, ]8 d - mysqli_query($conn ,'UPDATE ta SET id='.$id); & q A4 F, U. F' ?7 R+ z* B7 @
- } & Y- A1 j; t8 h4 O& a
- # php的文件锁,释放锁
" n* u$ V3 w3 D& L6 C' b - flock($fp, LOCK_UN); / z! S6 o- x7 t
- fclose($fp);
- U }! C+ z# N6 T! ]) T - - T% \. O3 \! z: M3 p; r7 r5 B& W
- // $res = mysqli_query($conn , 'SELECT id FROM ta');- Z5 s, X1 E/ S+ k6 D+ O7 ^* V2 b m
- // while($row = mysqli_fetch_assoc($res))
4 y) `8 D) R! Z" L' \! B. E) p& g - // {
. u. d! D! L# ~3 W, M1 z2 |) y - // $id = $row['id'];
! T# N5 V! k O* I8 Y4 T0 W2 a - // }
4 p+ e! l. V" C* F# ^; V - // echo $id;
复制代码
# [, S0 o Y1 K7 I3 N
0 P2 \- T7 E2 M2 [抢券活动实例: - public function envelopeSnatching(){3 Y9 |; p5 s; {! r0 s- U
- $lingqu = $_POST['type'];, B; s; { H4 V+ H
- $uid=session('u_id');//用户id6 `1 G$ d! p- Z6 ^/ _3 I; |2 P
- if(!$uid){2 ?* L! d% j9 [, ^
- $data['msg']='您没登录,请先登录!';
8 v9 L% Y& y, E1 U0 u) S7 X9 t - }else if(date('Y-m-d') != '2017-12-12'){
" R5 m8 R" i" X: l8 d$ z - $data['msg']='不在活动时间内!';% \ B3 m( }4 X! F5 v" l
- }else{: x4 G8 A6 i V1 E" P% I( O2 X
- $hours=date('H');//当前小时数$ B5 u' B& X3 c, m5 Z
- if($hours > '09' || $hours > '17'){) S$ E' u+ F1 `1 l+ X
( I7 T8 D9 p; U- if($lingqu == 1 || $lingqu ==2){//点击10点的) ~0 G# X) x( V" b+ z% |
- if($lingqu == 1){" M6 E" r$ Z9 ?" I) I: ?8 [0 h1 l$ Y
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
0 g% H1 Z) s% a% l' ]6 s" j1 A9 a - if($hours > '09'){
& S* R% m7 {$ q* Y# D4 @4 C - $num=mt_rand(25,28);//优惠券金额
% n% s C& {- @+ Z - $id=1;4 u/ p+ h4 m9 P4 Q/ H, R& X, P
- }
, A6 j- A" D2 H. Y9 j6 s2 d - }else if($lingqu == 2){9 l, Q( j% S- g( C5 B [6 d
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();2 Z4 i5 [7 `- B; X' K! \- d3 Y
- if($hours > '17'){
8 b8 e$ s$ F' r - $num=mt_rand(50,55);//优惠券金额$ G; D! X) W: ?. ^7 }2 D2 V
- $id=2;6 h5 x, e9 Z4 A9 v
- }; _5 [- s" n2 E$ Z! J# t: \
- }
: p% x- G' k% e. H3 \ - if(!$id){
; I4 B: V% l$ M& x- C- W - $data['msg']='时间还没到,晚点再来吧。';- P: ]1 L- X/ Z9 f
- }else{
( A5 j1 J9 B. x9 _ - if($is_lingqu){+ [! ~ Q+ b$ @" _7 Z
- $data['msg']='你已经领取过了,留点给别人吧!';2 L/ a* g- z7 @# p4 o2 u
- }else{
" X3 s2 l& t* Q$ p1 q - //锁表
- d2 B4 u$ z7 E) s- t- E6 p+ q - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');5 m( w( Z+ c6 e# K$ U( f
- $active=M('active_num')->where(array('id'=>$id))->find();
' }7 d" f/ T& ?; M6 i1 l; ~ - if($active > 0){+ [7 \2 w! d- q' M2 e6 a+ P
- //开启事务/ A2 _: K, R- [! E4 K
- M()->execute('start transaction');; ~" M4 h7 E% J$ f* J q
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));4 D$ ?' t a& L5 ?* H5 W, Y
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
9 ^5 a3 ?. \. p* a2 k - $members_preferential = M('members_preferential');
; }/ Z! g4 t$ x' H8 Y/ F" k! Z - //对应投资金额,
* U3 X# {2 t1 z; F+ Z( V/ d - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
# `, C$ H0 F3 \% Q$ A - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
- @4 l& m& D: R3 e( v - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
# m, R- m# N1 ~$ s& ~* i/ x - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券4 v9 H/ m* [) _. C2 ^/ i1 V
- if($save && $add && $add2){6 }1 [ g, a _7 C% V* W
- //事务提交
& T6 E* n0 r; ] G- t H - M()->execute('commit');
2 c4 n% A% v" a; p - $data['msg']='恭喜你领取了'.$num.'元优惠券';- b, A4 l- b& Z0 ?" O1 x
- }else{0 Q$ c1 I: m# O1 g3 P4 ^
- //回滚
1 T* a' q; ^ E/ t, m - M()->execute('rollback');
$ Z9 V& b. F2 S - $data['msg']='未知错误!';6 r9 Y4 a, [9 S% t/ g
- }
2 V# O+ ~& z* P$ y - }else{0 {) X8 W, T4 ?) L) X& z6 U
- $data['msg']='红包已领完,你来晚了!';% A% Q2 l0 a- E' D" @' o
- }" q) n4 f9 o+ ?$ z$ j
- M()->execute('UNLOCK TABLES');
. S/ r$ \8 f5 b: ~# I. _' x0 m - }
0 f: v" |% D! l I- @" a - }
# [0 S! g; e& D - }else{1 s1 `$ T; u: r3 J* v
- $data['msg']='非法操作!';, J( O/ S0 b d
- }7 ?; O4 R$ P& n* s
- }else{ `/ j7 _/ u5 t1 [' n3 Y
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
; K2 p' t! l3 T0 ?& S7 U3 f - }
; m* S0 v( x+ k* p; d - }
: h( z1 }& W) ]! n0 A$ F B - exit(json_encode($data));! J3 K* ?8 l# H
- }
复制代码
' ~- c8 r& ~* k
, C' _" P1 f: L4 ?: w! G0 o4 I |