模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
) b: j# Q/ B' D6 Q7 AMysql中的锁语法:8 t1 W3 O, \9 I- A
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
, L: b$ A/ G( m( H4 ]UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表0 w/ H, l, j# b( b, P% H
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞! w! j. U9 o% c: E
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
. _: X4 y0 \+ Z* ]. G9 C( N( S' ^0 X文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
/ K: @+ G3 o* V, p测试时,有个文件就行,叫什么名无所谓 总结:& }% ~; C1 G. p% G" y
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
, y- A+ D. m/ o7 C: a- ]* n1 F- N1. 高并发下单时,减库存量时要加锁
% b2 o: ~2 u' n! ]* ~8 ?" ^2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*3 S# m0 t, v) S0 C
- 模拟秒杀活动-- 商品100件* n! h6 ~; R j: i0 |
- CREATE TABLE ta
/ u- N. ^) ?( O8 q7 t$ z$ C - (/ b! K+ p! O7 o
- id int comment '模拟100件活动商品的数量'
- N1 k* r8 _2 ]- v4 ` - );, F$ n+ j$ q, C9 `7 p: [
- INSERT INTO ta VALUES(100);* c/ S5 W4 j" ^8 x0 i9 e9 E
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
0 L& s+ t; K8 B6 _. b - */
9 E4 L. A% C1 y+ f; K( i) s -
7 @0 f! x% x* i' l$ q" P) m+ `; x1 j - // 关闭错误报告
# }) u# l5 }. p$ H+ _ - error_reporting(0); * ?* @- E* @4 _, Y; _
- ! V$ N& S2 x" K% j! _& S% l
- $dbhost = 'localhost:3306'; // mysql服务器主机地址# }; T1 i7 O- t- _2 G& I$ L6 f) B
- $dbuser = 'root'; // mysql用户名
' |0 }; ?& b) `9 e- o" L, ~ - $dbpass = 'root'; // mysql用户名密码% J0 K6 c3 r% k7 Z+ c
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
' [0 ^/ {( q' @7 g - if(! $conn )
( V9 g1 g( V2 W. s3 F0 g1 ? - {
& B5 {" U3 _) F - die('连接失败: ' . mysqli_error($conn));
: M+ z7 P$ o+ Y/ Z- b) S1 [7 q2 K - }- I: a( F0 e' V ?* \ L
- // 设置编码,防止中文乱码
1 ^$ s# S N# Q! X6 r - mysqli_query($conn , "set names utf8");( O/ f, n+ i R
- mysqli_select_db( $conn, 'temp' );
9 L% _& J8 ^. `% f1 Q+ Y
6 o/ Q* c4 l v: X6 E- # mysql 锁
; k( J; R2 t% F8 J! | M - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
0 g* K4 M. r2 o9 M - $rs = mysqli_query($conn , 'SELECT id FROM a'); 8 K4 r! [! I2 a7 \7 C9 A, p
- $id = mysqli_result($rs, 0, 0);
8 i, J' F; t& H* p, `- J - if($id > 0)
- i. e% C" ?# |2 X - { ) g4 I) ]. Q. T9 U- _% b
- --$id; 4 ^: L) z0 W: V6 I+ D
- mysqli_query($conn , 'UPDATE a SET id='.$id);
8 R/ O) f3 Q% Q- t5 \( C8 b - } 3 q) g. l. s' k v5 U4 L: B
- ) v8 `, i( V* n8 t% @7 ?, O
- # mysql 解锁 8 l5 n4 G8 a2 k4 R2 A- ~" c; c
- mysqli_query($conn , 'UNLOCK TABLES');; l2 K% N8 u6 A7 j; a* `$ v
- //查询解锁后的id值
5 C m, M$ v( ?7 r& @ - // $res = mysqli_query($conn , 'SELECT id FROM ta');1 X( \7 J" j# N; h5 H7 v n' {
- // while($row = mysqli_fetch_assoc($res))! ^7 C) m0 k2 r) P0 i ?, [; H$ P
- // {
' l9 s1 y. b) _ R7 ` - // $id = $row['id'];
3 S8 C1 w3 O* T' a - // }
/ _8 A, |& M; v* E6 T# h( c - // echo $id;
复制代码
( b' b+ }9 g/ B* ?
4 ?, J5 w8 m4 w
2 [8 a* J* P5 Y0 q) Y5 V- YPHP文件锁示例: - /*
+ U" ~6 O1 k8 u# T" ^: G - 模拟秒杀活动-- 商品100件
% o/ x6 q% N7 u% ? - CREATE TABLE ta
8 ?: X( h0 C ~8 M" z4 y! E - (# c9 O& c. t5 K l
- id int comment '模拟100件活动商品的数量'+ n) v) S# P( M9 B
- );
: l q1 [1 K" s: W( T! Q" [ - INSERT INTO ta VALUES(100);& C+ R; @* V+ V2 @8 l
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
( V' z3 w- `6 n5 i- T - */ $ w `7 e3 t) a) g* F# l) f& Z
-
5 }- ~. @5 Z x* \ - // 关闭错误报告
) d; [; F$ V i x h0 ]2 q2 @ - error_reporting(0); " i; s3 }; i- k1 x
, K. R% _) g# f* n- P3 F7 s- B- $dbhost = 'localhost:3306'; // mysql服务器主机地址, A) }. |" `" S, s x2 {
- $dbuser = 'root'; // mysql用户名
' ?* N8 k& M2 D8 W8 @! } - $dbpass = 'root'; // mysql用户名密码 ?: A' C- i' u6 m% t
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
5 G; r+ O5 P( j - if(! $conn )' O- s) f& \+ C' {
- {
) l9 G1 M3 F. Y; r0 ^; i- ]* e - die('连接失败: ' . mysqli_error($conn));
$ G6 x8 t4 s7 H. h ` - }
/ s& x! X% {) f. K+ A! O# [ - // 设置编码,防止中文乱码
7 d* H- Q3 q1 F* j: D" j - mysqli_query($conn , "set names utf8");
( Q0 r# h7 M& U3 E6 K( F - mysqli_select_db( $conn, 'temp' );
. q8 W8 Q4 w) J: ] - ( o/ E+ u6 F9 U
- # php中的文件锁 * b/ X/ H! {+ W) A
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
, _4 B8 N9 F6 N* _8 E$ ^. ` - flock($fp, LOCK_EX);// 排他锁 W5 F* r, G/ P' N5 ^" J( v
- * Z9 e ~( V) D0 R0 L# v2 C; X5 @
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
8 G0 ^. u; m' { a t - while($row = mysqli_fetch_assoc($retval))
3 F. ^% u, {( o3 Z - {, V0 Z- {7 p4 Q% F/ N
- $id = $row['id'];! \6 L$ i* S: \4 k
- }
* }% F* I! _* w# | - : p" l) U" L, m, R: x5 q D
- if($id > 0)
/ ], C. S& N1 x$ M3 o" i! J - {
/ l E8 I+ Y# z6 l" U - --$id;
1 ~' R1 O0 Q/ N+ @2 n& N - mysqli_query($conn ,'UPDATE ta SET id='.$id);
, M" G; U& H$ Y$ ? - } % @5 ?6 _, l6 `$ P) u/ G$ I4 c
- # php的文件锁,释放锁 - } _1 A4 k3 ]' _
- flock($fp, LOCK_UN);
7 n4 w r7 _( f6 v2 y& G6 W - fclose($fp);
1 |8 f8 Y; d/ X2 a p O - 9 e. g# x9 w6 g+ n3 I1 j
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
' q2 P# t, |" j: Z/ E - // while($row = mysqli_fetch_assoc($res))9 R- {# K) N. \' p6 b
- // {
S, O7 U% o1 U% ]0 \' I) U - // $id = $row['id'];
6 y( ]! y3 L" _6 S: ? - // }
6 D0 P* J% Z) z( p; ?: S' ?4 E - // echo $id;
复制代码 9 J# {* q8 Z4 `2 Y8 C4 w8 ^
/ J p+ l: n5 X/ P
抢券活动实例: - public function envelopeSnatching(){1 x5 E7 H" E9 w1 d% j" R
- $lingqu = $_POST['type'];5 W: D7 a* \& p. `2 i# n
- $uid=session('u_id');//用户id
; q, f! Y5 V8 C$ z) }9 l - if(!$uid){: n! t6 {. O/ M; O7 u/ r
- $data['msg']='您没登录,请先登录!';
2 j& ~6 J! g7 t$ ~ - }else if(date('Y-m-d') != '2017-12-12'){; m2 f: S) C0 C4 ?
- $data['msg']='不在活动时间内!';: i* A' ~# F* v0 a; g2 @4 o
- }else{
$ E$ i/ g# j0 c4 z5 v - $hours=date('H');//当前小时数) `' i* c, K9 g) f- ]/ H* e8 o9 ~
- if($hours > '09' || $hours > '17'){
( Y$ S4 g c9 c3 b2 a - ' O3 B% \5 m9 B# M B+ S& Y- p
- if($lingqu == 1 || $lingqu ==2){//点击10点的
) C! R2 d) Q1 O3 d - if($lingqu == 1){2 x2 j& q0 w! u2 M2 ?. E. H4 h
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
) n/ [' X# ]" p/ P% s) T - if($hours > '09'){5 f: \$ G+ ]- o4 W3 Z4 O2 x/ ^! N
- $num=mt_rand(25,28);//优惠券金额
. s% ?' m4 T9 Z. r4 D! [7 W; U9 w) ~ - $id=1;
( R, H: z3 L( g$ J" o6 \, w - } g) F) g: `! X- O& r8 _/ {2 C
- }else if($lingqu == 2){) [% W! p( r6 T3 E( P) Z0 `
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
* a; F$ l. c: h0 {. o8 x5 _6 Q8 q - if($hours > '17'){) n9 d% L. H! q+ b$ I) t2 v, {
- $num=mt_rand(50,55);//优惠券金额, u% a; D0 A7 }: ]
- $id=2;" a Q: c4 y. s& Q
- }/ }. U* A, J/ J5 X& K( M
- }
" p. X7 p' ^# l1 F - if(!$id){: Z7 J: t) u: U$ {- K
- $data['msg']='时间还没到,晚点再来吧。';
1 S" Q) x. W# s% ^1 u$ U: A - }else{- Y% r" O o' L, H- Y& _9 ?
- if($is_lingqu){: n- F7 R; m1 f
- $data['msg']='你已经领取过了,留点给别人吧!';# [" L% M" H, @2 J: T8 v
- }else{2 Z7 G G' ?+ l; p
- //锁表* B, A& J5 ~: n- Q! t6 A. l5 e
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
9 d8 e- Z+ k' o$ W0 p - $active=M('active_num')->where(array('id'=>$id))->find();
- L. Q# n- E! D, z* @% ? - if($active > 0){, m: j8 N. W5 L3 S4 `' p
- //开启事务3 k+ g. @9 M) z- T* h. [
- M()->execute('start transaction');: P) D2 F8 U, d# ?
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));; d) ^4 J) t2 K* ?7 t2 e
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
7 |+ d8 d5 l& u" \5 V - $members_preferential = M('members_preferential');; E+ V4 ^6 ]! y9 H# _
- //对应投资金额,
' R+ b; h' c) _3 `* u# h0 n - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));9 F% E( ]! i) Q
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
! r3 a3 G' G' ~ - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
7 n: }; E1 L" q8 w* k5 w$ S2 n# Y - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券9 M9 L0 F5 |- ]8 h
- if($save && $add && $add2){
2 m8 Q% P" B6 Z" m - //事务提交# T& X; g4 c; B6 |
- M()->execute('commit');
/ W7 c) Z: v2 }' Z% V - $data['msg']='恭喜你领取了'.$num.'元优惠券';
, x" a7 g) ^9 V$ V" } - }else{# S0 A y0 ~: _4 s
- //回滚
0 b1 P2 H. z @+ E1 ^4 p - M()->execute('rollback');2 l7 g' E: g& _. p: Q) X; |4 Q! E
- $data['msg']='未知错误!';
# L5 @, H( h; A& w - }
! r( q1 L9 r6 P8 p/ Y4 k3 T - }else{& |5 Z/ k: \' c; _
- $data['msg']='红包已领完,你来晚了!';2 Y; u2 G9 c- g N V
- }
0 E# G" L" z% _ - M()->execute('UNLOCK TABLES');- h- T+ }2 |1 y9 ?0 [! a$ k' E
- }3 h4 h6 l4 w6 y i3 O
- }! b, ?3 e! L% H+ r9 E
- }else{
o' H) `0 [6 T6 E - $data['msg']='非法操作!';2 {5 F# T: \7 y
- }' F# R* n) n4 b5 w7 u8 p6 j
- }else{1 J& {& f' m0 T* H4 x7 W
- $data['msg']='还没有到活动时间,请晚点再来哟!!';( n4 ?( L0 [+ \2 e7 z
- }; t. q. L0 A* b0 I! Q7 }
- }/ {# ?7 J8 X1 W, f
- exit(json_encode($data));3 @1 L4 u2 G' j9 s4 _% s' r: N |1 d+ ]
- }
复制代码
O7 B. ]$ ?4 A$ }! g
8 c0 O7 {. S, a F4 k6 l |