|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 4 w7 l9 R2 W' Z: Y" Y. M
Mysql中的锁语法:- X4 _1 u2 j# Q0 `
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】- B) \( k; T9 M" g8 ]
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表6 \1 |# A1 M* p: w- E/ z9 J1 v l! l
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞4 M6 D% _9 t) }8 [- R7 g+ o
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
! h Q( D# t5 L3 Y- v9 w文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。0 Z9 K% `+ {; w+ }5 z' n- h
测试时,有个文件就行,叫什么名无所谓 总结:* ?' c! ~0 l& B' T4 D
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
8 u0 Z5 y2 P# e& x5 @; P# O1 M1. 高并发下单时,减库存量时要加锁
* @* x+ ?6 E2 n2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
8 Q* G2 ^; n1 E9 Y& N+ ? - 模拟秒杀活动-- 商品100件
& b* B& B$ \/ l6 P# J' u - CREATE TABLE ta: f2 ?" u( q- T! l9 c' g# A
- (
. s d* \+ N1 J. t+ b( i - id int comment '模拟100件活动商品的数量'' M, c3 D3 D5 I8 J3 [+ J! E
- );
l$ q+ `* z" Q - INSERT INTO ta VALUES(100);
4 n1 n% C; T; G4 I% l; f2 a2 N - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件7 Z! f9 {9 o( s
- */ 7 D; ~; O* b- ^& n+ o
- + M: j5 k: L" y% H- R$ k e
- // 关闭错误报告 b* m- E! i' @1 R7 l, r
- error_reporting(0);
! `$ O0 `- F- O) V/ T& S5 b: E - 2 F6 ]: {" v; N, A5 a5 M+ L
- $dbhost = 'localhost:3306'; // mysql服务器主机地址* P' A+ m8 c" D+ Y" i# J3 P
- $dbuser = 'root'; // mysql用户名* B0 L2 _ p2 O% A- e
- $dbpass = 'root'; // mysql用户名密码( z& ~( }6 ~" q7 f9 e0 Y) S% F0 q
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);6 D7 `& N& b V
- if(! $conn )
+ D: o: C4 d" f5 G2 j* I2 r - {
5 S" _+ R+ F9 a& g. ^7 i, {" g - die('连接失败: ' . mysqli_error($conn));6 W; U0 W* c* R: l A& K% X% s
- }9 {* T, _2 [ [0 B1 J' S5 |
- // 设置编码,防止中文乱码1 B/ _- F5 h6 ?" Q
- mysqli_query($conn , "set names utf8");1 ~6 N$ P* {+ a' t
- mysqli_select_db( $conn, 'temp' ); c8 R. J; W1 q8 ^
- 5 l2 }& u( J: ]7 d; |$ B
- # mysql 锁
; r5 p' o) B! A& ^8 Y4 ?' K - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 ( Z/ v) h- d6 a L* @9 W+ Z
- $rs = mysqli_query($conn , 'SELECT id FROM a'); # S2 [! Q5 A( G) A+ \$ Z% ]7 K3 M
- $id = mysqli_result($rs, 0, 0);
! {* X. k& f# H% z& _$ P - if($id > 0)
4 J( v3 k3 E1 P4 F6 D$ v5 q - {
; [7 w0 v. W3 p. {6 H) @2 m) ?" E - --$id;
$ S9 l& }4 ^6 `! U/ [, H6 v - mysqli_query($conn , 'UPDATE a SET id='.$id); / V4 p y5 `8 J$ c- |
- } 7 b" g- h: j% n) t" M% e) B
- 6 L: j1 H) Q: y4 m# D
- # mysql 解锁 - d9 y: z% t4 I
- mysqli_query($conn , 'UNLOCK TABLES');
0 x4 O: `* o2 | K - //查询解锁后的id值
6 K* X( I3 k0 [9 q% B S - // $res = mysqli_query($conn , 'SELECT id FROM ta');
. J, f8 t- x& M6 `' p { - // while($row = mysqli_fetch_assoc($res))( K- j' U0 f: T
- // {& Y$ x. s1 ?' [' _
- // $id = $row['id'];8 |$ i! D& w; |( ]) l5 I# G
- // }
2 t7 a. W0 j0 I0 Z% q) g - // echo $id;
复制代码 $ r8 o6 w& T" q" C( X# S5 F4 _
, ]8 b( E0 y+ ~* e k" \
x% X- t9 M h5 IPHP文件锁示例: - /*+ |" b1 t- J }, b
- 模拟秒杀活动-- 商品100件
, Q8 ?/ o0 ^7 i# j - CREATE TABLE ta
" F- p7 |3 S2 N4 i% O% k( z! _ - (
3 [0 J- ^9 B" G W7 H - id int comment '模拟100件活动商品的数量'
. H7 m8 N7 E8 z' M( t. Q6 z" q - );
w) {$ a! {) s0 R. ^ - INSERT INTO ta VALUES(100);1 P' D8 W4 {& P: p4 {
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
, w( t0 ~5 v* a5 h - */
- L- u+ m0 J" b; B7 g9 I% B' | - 6 v, i* U/ E7 O0 k Y# o9 f
- // 关闭错误报告
( F5 m* G0 Y- z" Y; `8 v& F - error_reporting(0); 2 d$ ]; B- }# r9 a# p1 t Q$ z( D
! C& y8 N) B6 N6 I; G( I- $dbhost = 'localhost:3306'; // mysql服务器主机地址
6 s" w6 `+ N% D - $dbuser = 'root'; // mysql用户名
9 r0 _' R7 k1 f6 b7 U: f& V5 T - $dbpass = 'root'; // mysql用户名密码
- t$ A4 F# U) r2 f - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
+ V. G) B8 |7 J8 }- X - if(! $conn )* D- F" M4 |8 G- T: ]7 R+ R1 i
- {; S$ ~5 ]! Z9 X0 [3 F# ^
- die('连接失败: ' . mysqli_error($conn));1 s' [) }! z; `, T2 ^1 L
- }
2 ^6 t& f; u' t5 b3 W - // 设置编码,防止中文乱码
8 G/ k% t7 p& [7 Y! r- R$ z - mysqli_query($conn , "set names utf8");1 _6 X' g |( n- m1 l: c4 ]/ \
- mysqli_select_db( $conn, 'temp' );
5 r1 `+ d- W: e. o. C# P) c2 j - / |" ]6 U; ~5 D/ @# j
- # php中的文件锁 " h: S7 F5 v& f. S, e7 |) V7 w! |
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 - y: o6 `) I) W# r- G7 p
- flock($fp, LOCK_EX);// 排他锁
7 d1 ~- S5 s5 q# |
0 V! e6 f3 E( A, R5 d* s: G S- U- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 1 `0 Y, c- o8 O* Q. ], }0 f
- while($row = mysqli_fetch_assoc($retval))! S1 f, J4 |0 `# I# }) \
- {
" w4 M8 j7 \' n) O4 L9 q9 r - $id = $row['id'];
" Y: P% q' u% g) N0 b - }
8 C1 R# J" N# n. U/ x- @ - 8 C8 q3 V3 V; F2 k6 @6 S# M
- if($id > 0) + Z/ i( X5 T. A8 H
- { 5 D h- W$ O* o% c: p. |
- --$id;
0 e' t9 D) Y& b1 k! S) n - mysqli_query($conn ,'UPDATE ta SET id='.$id); , r5 F4 u% I/ Z Y) L2 b. @
- } 2 Q( W$ d+ g% ~! u. X0 {6 d; e
- # php的文件锁,释放锁
. I" p( ]) D% n' A- c, b: i - flock($fp, LOCK_UN);
- W# s, |0 E# x - fclose($fp);7 B* K5 h8 u* B4 P# D' d% F
@1 y* A2 {/ Q( O, ~3 v. v- // $res = mysqli_query($conn , 'SELECT id FROM ta');
4 l. C$ |* w |0 h& A1 ] - // while($row = mysqli_fetch_assoc($res))
8 f; X' ` X# n5 x - // {
6 | a1 d+ W5 `( G3 v - // $id = $row['id'];
0 ?( n. \: q/ c8 ]7 H4 y - // }) D% g6 d0 C Q9 v3 G
- // echo $id;
复制代码
- K3 D& B: L# J* y' E
7 {+ @6 H5 x& W抢券活动实例: - public function envelopeSnatching(){( T' ?* s/ a) G/ U
- $lingqu = $_POST['type'];
! u R" p1 R8 i0 L - $uid=session('u_id');//用户id
B, D4 c; T3 l# L5 v - if(!$uid){: X( G. D$ K8 B5 O9 ^2 Q% I* G
- $data['msg']='您没登录,请先登录!';
1 m$ [' s, e/ c3 x' M - }else if(date('Y-m-d') != '2017-12-12'){& ^ [1 Z9 ^6 S- c
- $data['msg']='不在活动时间内!';
6 ]% ?2 \8 W$ ~8 Z1 q- U+ y/ B - }else{
1 _ l- Q- }: ]+ {7 T8 X - $hours=date('H');//当前小时数0 o! H. n0 w7 |/ X8 K$ l [& L
- if($hours > '09' || $hours > '17'){
# y/ c8 ]1 M6 V4 e* o* n - 7 a. E% S: A" q# d" d e+ J
- if($lingqu == 1 || $lingqu ==2){//点击10点的
8 S. w3 L: b. k# n3 w- g - if($lingqu == 1){7 ~1 S1 k6 u0 a5 H4 [+ f2 @
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
) o- U1 g7 l' B+ P, r! J - if($hours > '09'){
" y/ P( T' w. b4 }% U" }( W - $num=mt_rand(25,28);//优惠券金额6 Z+ ], }+ R' Q0 l
- $id=1;
! N1 a+ o4 _6 b/ K' I - }- Y* Y2 g! I% C. ]3 \2 P2 C2 q1 \ M
- }else if($lingqu == 2){
9 j' Q& i. N$ B - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();& s% |1 E2 C& {( c& Q
- if($hours > '17'){
& c3 o8 S- D% s% q6 J6 f - $num=mt_rand(50,55);//优惠券金额- H; |1 P8 g; Z- t5 Z( w
- $id=2;: r* I7 r8 w; R, m1 j
- }
" U( G! r0 ?6 d. h+ `$ a - }/ O) [) H; {# Q8 _4 x
- if(!$id){. `1 z- U# |2 I- ]
- $data['msg']='时间还没到,晚点再来吧。';6 C$ {6 g( ~2 \% U7 M
- }else{
: {4 t9 b4 O) C2 b- J& O2 ? - if($is_lingqu){% o0 y4 Q6 V" E3 ?, S+ o
- $data['msg']='你已经领取过了,留点给别人吧!';
3 e) J& A0 o# m$ G3 m1 \ - }else{6 d7 s( } z, B8 f# a) P8 E7 f
- //锁表
, K! I9 B7 v# G - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');/ X, e3 D, z! J! u6 i( K6 n: {7 I
- $active=M('active_num')->where(array('id'=>$id))->find();; q( Z( Q5 r' X" {3 v3 |
- if($active > 0){) g3 [8 e8 a8 k1 F+ D9 u
- //开启事务
0 p y3 q. n0 P% e5 E: I" I2 h - M()->execute('start transaction');
' r. k: Q. r, X" U- o& |9 h - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
' u: }; ^; w. _) h4 O - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));* c7 p. ~% }! b" A6 ~* x1 X
- $members_preferential = M('members_preferential');
% B& u7 B7 s8 ~9 K# N" y) g \4 P! F - //对应投资金额,/ V9 t5 y& B0 W* X, {" Z* p& d
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true)); m& W/ i* N$ R& K
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');" ?& _9 t4 @0 c, m- Y: v
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
- k# ~/ m( `) i: U# ?# m. y# | - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
1 _9 t" T$ K, q8 E! k' e - if($save && $add && $add2){
2 A& Z! l; A/ F4 U - //事务提交& C `2 C9 l( \
- M()->execute('commit');
7 U0 o& c1 I- @ - $data['msg']='恭喜你领取了'.$num.'元优惠券';
6 g( f5 p0 T, p - }else{% e6 S- ~6 t3 ^, Q t8 S$ N4 [5 q. }
- //回滚
: G3 J3 B5 `6 ~0 g% i - M()->execute('rollback');
- j2 e, d( U, K - $data['msg']='未知错误!';
2 W8 d# G R, ^! u2 l2 C - }
~" n6 Q# G8 s9 \3 c - }else{
8 ^$ k) Q* l$ @1 u& o2 }* }. x& I5 G - $data['msg']='红包已领完,你来晚了!';
' f' f9 Z- ^, b" x H. ?/ U4 Q - }
8 i, y" q: K: K7 j! |& D - M()->execute('UNLOCK TABLES');
4 P; g/ p( U$ i2 l - }
9 L& Q5 z$ s4 b e: t - }
4 U# ?& `6 }, T ] - }else{5 I) i( N- X. c
- $data['msg']='非法操作!';
( _$ N# K2 e. @# V0 q - }4 g) b) m; O$ R- e @
- }else{# v7 w7 M) W1 R$ T+ f( {0 ~+ z
- $data['msg']='还没有到活动时间,请晚点再来哟!!';& f; K/ }+ W; |3 D j1 a
- }' q. L; V! f3 r0 o3 J8 `
- }# H5 P6 L1 \: k% s, ^6 U! ?+ F
- exit(json_encode($data));
/ i* P1 Z% o4 g0 c/ M - }
复制代码 $ S0 [- t( b/ S, w$ V, S: H! O
( L) N& y4 H/ @# ?0 \ |