模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
1 G+ L/ Q* A$ g3 I z* ]# VMysql中的锁语法:" j! B8 a3 j% h
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
* T+ W( O9 j/ ]5 c7 x0 W! aUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表 b! ~) l" x" q$ M5 f1 ? h
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞2 a( G& b& r1 g' M% }( E0 J
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
( f& r, Q. o1 l文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
8 S7 g' O* n% v; ]测试时,有个文件就行,叫什么名无所谓 总结:1 X+ L8 d9 q3 b0 o- k I
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
9 X G9 N: r! t/ c1. 高并发下单时,减库存量时要加锁
3 x$ D+ m8 Y& K/ Q1 m; Y2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
* X: U( N% b( g$ }0 r' r% C! j9 E - 模拟秒杀活动-- 商品100件
; W# J8 [, ^+ m! k/ N/ y - CREATE TABLE ta
8 u r/ d! h7 w/ L5 U J - (
t. ?( @' b9 c0 `) b& ` - id int comment '模拟100件活动商品的数量'0 c1 \* S* ]% l9 i0 i( ~
- );7 o7 n9 C. o% V4 e% ~5 ?
- INSERT INTO ta VALUES(100);
% q- t( }# _" f4 k$ v - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
& d5 _! ~+ o" R% v9 b - */
* N; W4 I% w X; E% b+ F - ! Z! m' ^! \9 m, U' u7 F
- // 关闭错误报告2 B- w! \8 C( e: X# F8 ?
- error_reporting(0); ( n0 X6 L% @2 {9 @4 Y
5 n* \$ R6 R2 w- $dbhost = 'localhost:3306'; // mysql服务器主机地址 X+ k9 o4 p+ r7 {" f
- $dbuser = 'root'; // mysql用户名! ^# C. ~2 S# V& u
- $dbpass = 'root'; // mysql用户名密码
2 B6 {8 r9 a9 C( T9 X! D - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
, ]0 e! M4 c, G) O9 O! _( P - if(! $conn )
& F$ N6 b. _" e2 Q8 j; \6 {! b - {
& N8 K1 U6 X& o: O1 U5 N8 R2 Z# Z% S - die('连接失败: ' . mysqli_error($conn));' ^; p, |8 c+ y* ?; o! |& ] S6 `
- }4 y* Z, E6 r; C! T$ g
- // 设置编码,防止中文乱码. a- q1 T/ I% @! c1 d
- mysqli_query($conn , "set names utf8");
" W3 v0 y% L7 e! w - mysqli_select_db( $conn, 'temp' );
: a2 T( ]4 h& `# y5 I- l: {/ F - " J) m0 ^% O/ U/ {# _. X" N
- # mysql 锁
& ]; I" H) p% o' O7 ~ - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 ! p# X3 _/ u2 w$ z- Z
- $rs = mysqli_query($conn , 'SELECT id FROM a'); - V- H) `, M5 f4 E4 z* o
- $id = mysqli_result($rs, 0, 0); . ]/ g2 G- s' p
- if($id > 0)
; K; L" U9 s: N+ b' l7 M, H: H- ] - { 6 M- a; A7 T0 S4 x4 ]. W( x
- --$id; 0 q! _5 l6 H6 Q' [1 n
- mysqli_query($conn , 'UPDATE a SET id='.$id);
# g/ O. Z; K; `0 f4 o - } 2 ?) E9 A6 ^+ Z* ?1 M# }. I
- # G2 G# v. I( \: Q* [
- # mysql 解锁 8 L; t: ~6 n- [% R2 Y$ w
- mysqli_query($conn , 'UNLOCK TABLES');2 J* v" o/ R2 b5 d. A" e
- //查询解锁后的id值$ x! }$ S, e0 M, [. b( d
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
: { \, f. G* ?( Z - // while($row = mysqli_fetch_assoc($res))3 ]/ F I1 _. Q+ P. `& o
- // {2 I7 [) |7 A& Q0 x7 K* k& ?
- // $id = $row['id'];4 c" r/ }- ~$ e
- // }
6 _8 U6 j8 `% R) [) ?, X [; ~* X. O - // echo $id;
复制代码
; D; \* I" A% N( W0 M, ]% O% V' i, D" P/ \+ G0 [- [6 ?
6 @% o0 c; p* S( }
PHP文件锁示例: - /*
d. y4 q- o1 b7 `8 g - 模拟秒杀活动-- 商品100件3 `6 l0 x% O' m# ~# D
- CREATE TABLE ta
+ m: I; G1 P: H5 v" b* M& c - (: G- ?. q* a: E% o: d
- id int comment '模拟100件活动商品的数量'1 @. N/ C/ z: Q2 Q2 U+ v$ O
- );4 u; M2 l8 o, x+ ?, T7 X7 a6 m9 i
- INSERT INTO ta VALUES(100);
) d7 u' c) @7 Q I) s' d - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件1 ~8 t0 o- j7 D' R4 ~2 c& V
- */ + g# C' ^- e8 I4 d! R/ p x
-
# j: |! m( f% T2 Z - // 关闭错误报告1 E$ r: W7 y4 u8 [% k+ }
- error_reporting(0); ' R: `: I6 S5 |) E
/ ~6 r( ^) g5 N3 K- $dbhost = 'localhost:3306'; // mysql服务器主机地址
/ r4 Z7 I P- _5 ` - $dbuser = 'root'; // mysql用户名# d+ q3 p5 U1 R. R; c
- $dbpass = 'root'; // mysql用户名密码
; H+ ]9 T3 I( G- v2 W0 y' S+ ?+ {& @ - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
$ ~) g4 F/ }' U2 L5 Q. R. f - if(! $conn )
/ x+ [ A% I& t; s9 v$ y# q - {
- q% q- g1 `, _: j) } - die('连接失败: ' . mysqli_error($conn));2 x3 P# [3 e7 b# U3 C
- }% S% H% ]2 q, m6 l$ v
- // 设置编码,防止中文乱码$ Y9 ^) s G+ K8 [ G4 V: C
- mysqli_query($conn , "set names utf8");8 J$ S: s# m* c- y
- mysqli_select_db( $conn, 'temp' );
! A3 `" K7 {* Z& w; k' i
1 t5 n7 g5 n( T9 q! g' Y9 z9 s' d- # php中的文件锁 $ ~1 b# X' D% Z& Z5 J' v+ Z
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 4 Q* M7 @6 e7 {, u7 S0 N
- flock($fp, LOCK_EX);// 排他锁 ) i2 y# U( X h
- - ]) ^, M5 I3 d/ |
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
; r/ i& @+ w% i - while($row = mysqli_fetch_assoc($retval)), c0 K& h+ u t( V9 i- _. C1 Y
- {( X" i+ ~. c# c( n& [
- $id = $row['id'];, u; m# N3 l9 T9 v4 {. B: x
- }
2 @0 [* b' r* G( E5 D - 5 ?$ ^" _% F6 ] `9 m/ c
- if($id > 0)
* {+ y+ Q& c. w - { 2 b# \& K7 o; d! P1 I. r$ j
- --$id;
' U: ?3 P/ f3 v2 S8 M8 s - mysqli_query($conn ,'UPDATE ta SET id='.$id); 0 _4 z3 u: r0 m5 b" J, L( X' [% N
- } $ e1 K3 u8 E8 R2 j
- # php的文件锁,释放锁 " G3 X9 M$ k5 I5 Y
- flock($fp, LOCK_UN); I, e8 F# @; k- p
- fclose($fp); t- e, j* H J. [
' C5 ~6 Q G: e+ |9 H! u- // $res = mysqli_query($conn , 'SELECT id FROM ta');$ ^$ O# e% {2 `7 f* j
- // while($row = mysqli_fetch_assoc($res))
3 J+ J) a, E) [5 i/ P/ A - // { [7 T. @7 ^; n
- // $id = $row['id'];1 m- {0 j) Y h1 V K) |
- // }
# n% M% x+ H5 g# U - // echo $id;
复制代码 4 Z0 a8 k* L3 m: _; ^5 ]8 H6 a
- M5 X9 [6 {+ Z+ `& L$ Q9 I' d2 J抢券活动实例: - public function envelopeSnatching(){! K) [) b1 G8 W7 t2 S$ B2 f5 A; O
- $lingqu = $_POST['type'];
7 F; ?8 |) f# Z+ I$ U - $uid=session('u_id');//用户id
9 P @1 V9 E! e: Z+ { - if(!$uid){
: L% ?+ X% v% D/ T0 k* M - $data['msg']='您没登录,请先登录!';
2 h4 k/ @: [; {" ~1 @9 W! ~' ^ - }else if(date('Y-m-d') != '2017-12-12'){
% Q9 O ?; u! o0 ?, x - $data['msg']='不在活动时间内!';8 s2 y2 @& \% e @1 j0 n- z
- }else{. R; V) t9 U% J7 G' J! E7 g* ]
- $hours=date('H');//当前小时数6 i6 V1 |# T. \' y, L
- if($hours > '09' || $hours > '17'){
9 B3 ^1 K# Z" {) B$ `# a# ^
; s W, _( z6 H# E' @- if($lingqu == 1 || $lingqu ==2){//点击10点的 N2 Z4 I' [& g
- if($lingqu == 1){8 a: n* l. E! p$ U
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过2 a, f% R) t# N$ m o: n
- if($hours > '09'){
: V" _) z" V0 M ^ - $num=mt_rand(25,28);//优惠券金额% O: T K, H! P6 X) e; `
- $id=1;
3 V5 g3 s% M% V1 N* W) K5 L - }: G8 Z. C% n) ? x9 C; q5 I) F' M& z
- }else if($lingqu == 2){
5 J0 b/ v0 t. ^ ^0 f - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();/ Y! v& |- A* n# p; e/ ^: f4 b
- if($hours > '17'){( P& N2 m4 ^& O; i# K- O" H# F* T
- $num=mt_rand(50,55);//优惠券金额- h% v0 }( [2 j4 D) b( S
- $id=2; [! n. a+ r( t* p8 ]: ]9 I
- }
. Q2 A$ @/ j0 d - }
. f# m' s6 h9 W$ y4 K! V4 `- @ - if(!$id){
* B$ C. D6 o% N3 c9 Z% ] - $data['msg']='时间还没到,晚点再来吧。';
' F" C5 m g0 G. s6 I - }else{, l1 a5 @' U* m' C3 g
- if($is_lingqu){/ I0 y* c9 A. z% r
- $data['msg']='你已经领取过了,留点给别人吧!';. P6 m" n- a( y; c5 J- y
- }else{' y; v6 T- ^: H, K3 G: M$ s3 A
- //锁表
7 F* E" t" O+ @% g- k. S; n1 z - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
/ Q6 u0 i( w( A8 W* C5 `- k9 K% X! x - $active=M('active_num')->where(array('id'=>$id))->find(); R6 C% S: ~5 B4 i5 X/ t& B$ _
- if($active > 0){5 A& j! P$ q, F" r
- //开启事务
# u+ H7 V: ]4 J - M()->execute('start transaction');
6 Y; a0 [- t2 I7 A - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));7 W4 B, ^0 e0 o0 a; ^, _
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
/ X' K, T+ Z- e# Q" c - $members_preferential = M('members_preferential');
( X& ?- s9 P6 V I - //对应投资金额, e& o3 p( u9 X5 i* J6 J. N
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));0 y8 F8 D Y2 L
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');: N& U9 `$ n; K7 b
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
; H4 T( J. v4 B - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
. `% u: a- v* z4 T; X- U( \ - if($save && $add && $add2){
3 b1 ]' a/ k) `1 C0 ] - //事务提交5 G! }$ D! r7 a8 N. l9 B
- M()->execute('commit');
/ S+ ]/ @3 c: B - $data['msg']='恭喜你领取了'.$num.'元优惠券';
3 T: \* p# i9 a' ^ _- d) ~ - }else{
; P) k9 A# C* }; R7 P2 Y# F' H+ j - //回滚
3 _& [. W3 y) A+ w( f% b - M()->execute('rollback');
8 v! r9 ~7 p# j$ [; b. u - $data['msg']='未知错误!';
* J9 \4 k) \% q. M5 C( Y - } n0 o( Q# n3 {. f
- }else{
8 ?0 a7 d5 T& m2 x, f - $data['msg']='红包已领完,你来晚了!';" T7 C; y. a+ r S" C
- }7 O; U- Y( N! m* }' j' R; f; L
- M()->execute('UNLOCK TABLES');
T. b: e8 x; n2 H4 A1 @ - }& `3 V" z) G& m+ ^- o4 f% [6 ^
- }
, L2 f* l; R! ~* _1 A2 c - }else{: u; \ r* P* u0 O- ]
- $data['msg']='非法操作!';/ q0 u+ P1 u. }0 d/ p; |
- }
1 n4 B' ?$ V3 s - }else{
5 Q" e" U8 f2 ?! M7 i6 Y. l. V - $data['msg']='还没有到活动时间,请晚点再来哟!!';
, v' e* c7 H9 T6 w - }
8 s Q F# t1 b K* `1 }7 w/ r - }
0 k5 I3 Z7 ], s* r& O a$ K2 B/ M - exit(json_encode($data));
" k. x! H( N6 J9 f5 ` - }
复制代码 $ n( w! s# a* r/ [( K n9 Y$ R
8 g5 f: h h8 @$ Q4 o |