|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
3 r+ e6 ]8 f b. D: E" o' vMysql中的锁语法:( J* N; @6 K/ H+ m! Q! W
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】2 W2 z3 M$ Z: Z. ^" R
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
, C: ?, q& P6 T" M+ jWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
2 b0 Y& q- R4 N# ] n5 z0 X7 i9 G注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
, E7 D% `3 O4 g2 f5 ~文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
* R; P1 I1 p' R测试时,有个文件就行,叫什么名无所谓 总结:2 S4 t$ [' j5 f
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
/ w+ Y# ~6 I, h$ F4 b5 b! A9 x1. 高并发下单时,减库存量时要加锁# S7 i+ Z; O& S; u8 B* S1 X
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
$ C0 O& ]9 o2 ~, y- {6 \0 A8 B - 模拟秒杀活动-- 商品100件8 p6 y9 K5 _8 }
- CREATE TABLE ta
$ m7 H% J- O8 U9 c3 h; { - (
1 Y% v' B, s- F1 a - id int comment '模拟100件活动商品的数量'
; U* O/ a6 n8 L1 Y - );% w: O! K9 S( a$ c9 X
- INSERT INTO ta VALUES(100);
* p1 b N, ?- Y* A - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件5 ^2 L4 d0 [: G0 O7 @
- */ & [- E* D$ Z u1 ]4 H( R/ m7 ~" J6 U
-
. o+ Y3 I# c1 f7 a0 ^( \ - // 关闭错误报告1 M" b1 I9 |& E
- error_reporting(0);
% m' I$ |) a: w, X5 K6 \, d
8 M6 C1 z3 L. l- $dbhost = 'localhost:3306'; // mysql服务器主机地址( L, T3 ]$ P2 D2 |
- $dbuser = 'root'; // mysql用户名
% z4 O1 c! R& E1 @. o$ I' V0 U2 X - $dbpass = 'root'; // mysql用户名密码
' e: Y% G. @" v% K! p* A$ s$ g - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
( k1 z( s1 {6 I O7 C, u+ \% z# M6 D - if(! $conn )/ L, D; E. @$ `: B7 ~
- {/ }) c1 [7 L: z ^1 S N
- die('连接失败: ' . mysqli_error($conn));- _0 x/ z, j1 l _$ x) n& `
- }' I3 {$ C) n- k# i, G9 T
- // 设置编码,防止中文乱码
/ ^. q: v7 s7 V - mysqli_query($conn , "set names utf8");
7 c* N" V2 O4 L$ Y' z0 R - mysqli_select_db( $conn, 'temp' );
( ]6 [7 v* C3 Y) h# x - ' {% P: d5 s; C8 G8 S# w7 S# v' h
- # mysql 锁
$ D; Y- }5 p9 p ]# L- ] - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 % K& A' S ^3 T% X: y2 n3 h" [! H0 m) z
- $rs = mysqli_query($conn , 'SELECT id FROM a');
- c' J+ O* p% ^6 y( h4 W. G' w - $id = mysqli_result($rs, 0, 0); ; c1 V+ j5 g/ y: M9 W+ X' \
- if($id > 0)
5 K8 [0 ` w2 T) ^. s2 _; z2 G' l - { # I- Y' s+ Y! s2 g3 m: M9 X8 K+ V
- --$id;
% P' t+ X; q& n5 g4 L8 w - mysqli_query($conn , 'UPDATE a SET id='.$id); * I7 x5 k% ?4 M8 f
- } 4 a2 V/ b8 S: L; F: p8 g% G F7 |/ \
- v" I9 }- p. x5 Q4 R6 e {: x
- # mysql 解锁
, {1 ?- v1 r) B5 i2 ~# X d - mysqli_query($conn , 'UNLOCK TABLES');
2 b9 s" X& n# t- R/ v9 I6 k7 Y - //查询解锁后的id值9 T/ n$ L& |+ @! X8 \; v& o1 f0 q
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
) A7 K7 Y( K P# k q - // while($row = mysqli_fetch_assoc($res))* K8 I' L/ \! m/ T( I
- // {
0 W% D6 f! d/ o, v; L# \0 v/ d/ b - // $id = $row['id'];
# T% O# }9 m, b - // }* u. B [& [0 b H4 C+ {
- // echo $id;
复制代码
+ [/ _& c M: |' n2 j" I: o. q
) p- ?5 S V& T. U# D/ t; q
" Y0 p4 ]1 r% y, W9 k$ iPHP文件锁示例: - /*
! d" D; p2 G# l6 u" M. {% X" K, B - 模拟秒杀活动-- 商品100件" X/ o6 V2 W Z2 Q( ]$ V
- CREATE TABLE ta
" Y* i7 c" Q: K9 s, X; V - (4 {) Q: f* Q+ h
- id int comment '模拟100件活动商品的数量'
, r' v" O( X! _8 s7 p( S - );
- k' H2 @" q# p( h* q& d8 g; p - INSERT INTO ta VALUES(100);% e7 R0 k. }0 H/ m0 ?1 c
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
% L$ g$ _; Z5 `* o - */ $ x2 r/ P: g$ Y5 }/ D
-
* C& M$ J9 V+ l+ v! Z8 i% D5 F, i - // 关闭错误报告
, D" x1 F, X. R, n4 F9 c# y - error_reporting(0);
# S$ }$ j! X r, X4 D. |$ W. H - 9 }6 r1 P' r+ b, G
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
# T: O d& n( O2 x: m. b - $dbuser = 'root'; // mysql用户名
7 N7 q: h* ~8 v) D& d% s6 ] - $dbpass = 'root'; // mysql用户名密码6 A' y, \4 F, d6 P5 J% v
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);9 F+ `8 p1 h; g" o& o' w: {
- if(! $conn )
% ?& B; j. S0 g- {0 P9 N- Y - {5 v9 l4 S1 |1 n3 g. M! x4 E
- die('连接失败: ' . mysqli_error($conn));: K4 }# {) i; [$ n( u4 ~ A
- }
' f$ S. y9 h& [* c6 e3 P: I3 A - // 设置编码,防止中文乱码
1 K( c; u1 J. ^: g& O - mysqli_query($conn , "set names utf8");- N2 W M' T. l4 J" j% [* W
- mysqli_select_db( $conn, 'temp' );
5 i. i1 h4 |- b2 P# \
! x3 g7 y2 [+ ?3 K7 O0 I- # php中的文件锁
( Z; G5 b! n: { - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
* [+ b$ P5 s5 g - flock($fp, LOCK_EX);// 排他锁 * W9 d8 y% e' I" s
- 8 D: h {2 Z! u+ V/ E Y
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 5 m% [6 Q# C# C2 v- _! Q
- while($row = mysqli_fetch_assoc($retval))# |" c3 k1 @8 ]; f" N/ \
- {
* ^2 G% P6 y0 A" R8 V - $id = $row['id'];2 `$ f: G( Q6 Y
- }
7 l4 C' }8 G: ]9 G5 S! c% h& s - ' d- g: ?6 g! B4 g; `
- if($id > 0)
: p' W# [5 s/ o7 w- `) ^) P - {
/ e* N6 h% {) R - --$id; & |0 E$ N7 o$ k4 f
- mysqli_query($conn ,'UPDATE ta SET id='.$id); ; `5 N* N5 @) ^) \9 b8 w
- }
3 V) p! C% N* K/ |9 ?4 m, o, M - # php的文件锁,释放锁 % M' B( h% c+ D; l5 w
- flock($fp, LOCK_UN); 1 p" Y* Z: U& M3 B
- fclose($fp);7 x l% N i% u- r% Z8 W
- ! I% I9 K% G$ a P* y d# _
- // $res = mysqli_query($conn , 'SELECT id FROM ta');$ u. h4 A' @% s: ]3 X5 A+ `, f
- // while($row = mysqli_fetch_assoc($res))4 `3 {% s& C' m) C" n6 e
- // {
/ Y/ m/ i5 J$ g ~8 X' f - // $id = $row['id'];+ @ c( ^+ I& H5 r ]; m
- // }
1 M4 R' O8 y I9 Q6 d - // echo $id;
复制代码 & d' p0 r. `% |4 P8 Y- s8 M
1 z( k( V8 q: L; V2 b7 I1 Y+ a2 {& {抢券活动实例: - public function envelopeSnatching(){
+ O. _9 F M* K& Z, G% C" a4 | - $lingqu = $_POST['type'];
. r5 Y" i/ \5 d; Y* b - $uid=session('u_id');//用户id
9 A+ V" L7 Y( l6 b8 C - if(!$uid){7 ~* p$ `) T; ~1 W! s
- $data['msg']='您没登录,请先登录!';1 r0 [% m. R) Y2 j) X' { L/ \( [
- }else if(date('Y-m-d') != '2017-12-12'){+ o/ h0 e+ z7 o( Z% x
- $data['msg']='不在活动时间内!';
+ j) I# |$ y( W1 g2 B. z) r$ v - }else{' k8 \, o4 u- o X' {7 {* ]! Q2 E
- $hours=date('H');//当前小时数
2 O* V1 I4 y/ I9 Z6 X, A" O' Z - if($hours > '09' || $hours > '17'){/ u4 I, ]9 x- F: e
- $ s; c1 X8 H$ L1 Y: \
- if($lingqu == 1 || $lingqu ==2){//点击10点的8 r3 ]8 ~# P5 P8 A2 [) `
- if($lingqu == 1){
0 ~* K2 f7 C! p2 ? - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
+ Q% y; A+ B, Y$ a9 c" E# X' ` - if($hours > '09'){
( G7 L, D" N$ D) K. ~- @, H - $num=mt_rand(25,28);//优惠券金额
: b: S1 t1 d3 G6 |8 D# ^8 {0 M - $id=1;
* ]9 A, E9 \3 l+ N! g - }- [, f e2 S" A/ `5 F/ r0 C
- }else if($lingqu == 2){+ a- x0 o n$ B5 o& o
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
/ i% m7 s1 W7 p8 U2 k& o, ^ - if($hours > '17'){
' _3 d/ |) }3 i \4 [ - $num=mt_rand(50,55);//优惠券金额
! X( U6 x/ |+ S( n7 G - $id=2;, g+ g o3 Q& s. b
- }: h# h- a' h# o S. r) y7 n6 \
- }
1 v! D5 C1 h0 @6 L7 R# U2 a - if(!$id){9 Y4 M# \3 K2 L+ C$ O- Z" P
- $data['msg']='时间还没到,晚点再来吧。';9 _5 \; m7 p9 Q8 y( ]4 G `
- }else{5 w) n I% q& C# s S8 l& n
- if($is_lingqu){* ?/ d, T: E- Q2 ~- m3 {
- $data['msg']='你已经领取过了,留点给别人吧!';; ~% J+ m/ `3 G! ^
- }else{: y$ E- I. J& ?3 W# O
- //锁表! U+ P8 B) g& Y! A7 A& g( ~
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
& |% A+ O4 P! @) c5 n$ y% s6 S - $active=M('active_num')->where(array('id'=>$id))->find(); V" J' {7 q {% ?/ S
- if($active > 0){
* l* U+ N8 z; ~6 \- R C. s3 @+ ~- L - //开启事务
0 w% t% ]6 v! j2 P+ }3 K- } - M()->execute('start transaction');
9 M& X* h( L& m8 V3 a - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));8 T3 x2 \2 @. E! B0 V& t) ^, _8 c
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
1 `$ c) }# g) `" ? - $members_preferential = M('members_preferential');2 R2 I) c- @) Q" |2 H* k9 m# a8 U
- //对应投资金额," P, _2 |0 |. ?' ^ w, i; }
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
9 N$ {( p0 y( {7 }1 T9 ^! @& B% u - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');+ P q! o9 |3 c) b
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
1 q9 ~0 u( C; ]* l9 ?% c( Z0 | - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券9 H1 [3 S9 Q5 v3 Z, c9 Y8 @
- if($save && $add && $add2){( \2 D" t$ t) S$ x
- //事务提交" H& S+ B' Z) r/ w& c
- M()->execute('commit');( [3 H. Z4 m) C0 x% }
- $data['msg']='恭喜你领取了'.$num.'元优惠券';/ C8 o! f7 D* S0 f# B
- }else{
7 p: \& P- V8 @& ~ - //回滚2 R$ K0 G: L' u
- M()->execute('rollback');. V5 m4 h! h; X
- $data['msg']='未知错误!';- l# B) j9 X/ o& @; F
- }
- t" O: p; K3 @2 a/ t - }else{
: g: p. G. D+ l9 W - $data['msg']='红包已领完,你来晚了!';7 j& \& @ D2 e2 s# G, [
- }
5 y: @1 P: b$ e, g: h0 t. F - M()->execute('UNLOCK TABLES');
u* _+ s* V# {$ D9 Y - }! B, v; ^" F/ G& b% a0 a9 F
- }
4 e! ^1 X5 Z. Y3 E& }" O - }else{; v0 p7 l [3 V9 w) `) E8 `
- $data['msg']='非法操作!';
. n- x- r7 Y& _0 |: P6 q - }
( ~: r' S1 u/ X& x- `9 d5 n - }else{
7 _# K8 A$ X/ |' F* g) D - $data['msg']='还没有到活动时间,请晚点再来哟!!';
# N, H, I7 E7 a - }
1 V! @6 k0 F0 L e. b - }" o! n+ r6 e. E2 i; _2 X5 I0 E
- exit(json_encode($data));$ O! ?$ g; \; F2 z8 J
- }
复制代码 ' |/ a) I4 T( g; W+ b
3 e9 y% @1 {% v; F' Z. z6 x
|