模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
2 K4 H' {3 }3 w, n& p5 fMysql中的锁语法:/ F& A6 E# h* N. S
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
; S ?5 p6 t6 r/ V) b, E( }+ NUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表$ ^, t# _7 W- n6 K+ v, v$ N, k
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞1 Y1 a8 z2 Y1 U
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
9 |3 b P) h' E: B4 b7 u% E文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
/ x0 J0 R& Q0 I" w5 O: u测试时,有个文件就行,叫什么名无所谓 总结:
! @! J- S+ E. Z0 j项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
6 O$ h8 M9 t9 k/ r" D) S1. 高并发下单时,减库存量时要加锁! N9 f, _# H7 t6 H* Y. v( v
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
/ p2 x( Q, ], f5 i1 M3 `0 I - 模拟秒杀活动-- 商品100件/ [% \. k. k9 ~
- CREATE TABLE ta
2 B: ~: l+ U. j! F# T9 U9 B' P! q - (
7 {2 D) U B1 p/ h - id int comment '模拟100件活动商品的数量'% K) y$ t6 |/ g7 L1 S U8 u3 F
- ); [8 Q- x' K: K; u! R& X7 Q
- INSERT INTO ta VALUES(100);( C7 j5 W8 L" q+ ?
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件: p8 O3 x8 W, v4 f5 [
- */ & O2 w. Y8 a" C k a
- & Z9 r' j7 b5 i7 |6 h8 g
- // 关闭错误报告" ~, y% z0 `) B
- error_reporting(0); 9 O! `9 Z- L8 D/ ]8 `8 t
\' k6 [7 w7 k8 z2 K% U' D% ~- $dbhost = 'localhost:3306'; // mysql服务器主机地址
& K' T" D- g+ { - $dbuser = 'root'; // mysql用户名
# e% m9 Q$ q- X' W - $dbpass = 'root'; // mysql用户名密码
3 y6 p4 S5 z2 n1 X7 _! M! K3 R# g - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
) j$ l( j3 a3 Q$ c" p! q6 r# K - if(! $conn )- j) b* ` b6 _1 B; B' L
- {
4 \4 b1 `: c9 t: U2 p# U, X3 N6 Q - die('连接失败: ' . mysqli_error($conn));
! V! W% C: t- m6 s7 j2 |2 x' { - }/ I2 c. s8 I3 { ~
- // 设置编码,防止中文乱码
- I4 |* U# T6 V7 X; \5 Z/ g2 X - mysqli_query($conn , "set names utf8");. @0 D; L1 v0 R8 ?
- mysqli_select_db( $conn, 'temp' ); " K7 A8 j' T2 |
- / S, S# y+ n- p. ~" q
- # mysql 锁
2 {1 b, E: r& p {4 U - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
( r# w! z4 u6 n- u! ?) o+ t" b1 ` _ - $rs = mysqli_query($conn , 'SELECT id FROM a');
- u/ o/ H' c# Y5 p9 d( H - $id = mysqli_result($rs, 0, 0); $ \! x, k5 m' C) E, r& m* N1 |( o
- if($id > 0)
' A5 t: ]$ ^+ W; y" s3 O, O% i) d# e - { ; ^4 L# |+ [" k7 b- F4 ]2 s
- --$id;
# p8 }- x: s! H: x8 X, \& T# ?( f - mysqli_query($conn , 'UPDATE a SET id='.$id);
% H; C, I6 S2 ]9 j, j7 k' Y) B - } ; s, u2 F4 a$ Y
' O' P7 Z/ t( J; I, H- # mysql 解锁 % A6 }: s3 l) c" t2 y- O$ k% C
- mysqli_query($conn , 'UNLOCK TABLES');
1 P0 Y L" u+ ^0 ^/ _9 M - //查询解锁后的id值4 v$ T8 M7 i3 P1 J) d
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
) o& J, P4 d8 q0 L f - // while($row = mysqli_fetch_assoc($res)), K6 ^, A. ]5 |! r2 I) S1 H( ?
- // {
% p* E- _. @( n& p5 F' O8 f$ d5 B - // $id = $row['id'];
( r) J3 Q7 H7 \. t9 u - // }
7 t. {5 x, \& {5 e - // echo $id;
复制代码 * C' d( c0 a4 `- p( C$ `
# y' t) r7 G3 @6 X L1 L, y! \7 L' y5 c; [" D3 I! w# V% A
PHP文件锁示例: - /*: V) g# u, p& D
- 模拟秒杀活动-- 商品100件
3 p+ |% y, X6 \ - CREATE TABLE ta0 N3 Y7 `9 w! M: [2 v5 A
- (
5 f( v7 f; A- M3 b1 \ - id int comment '模拟100件活动商品的数量'5 }" y/ ~. V! f. e2 |) z
- );: o9 a o1 n1 I6 Q
- INSERT INTO ta VALUES(100);
* T! B1 e: ]) _6 h: s- @. Y% y - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件# _, ^3 V" w" Q. {% j
- */
/ U7 @& O, S& i: a l9 x -
1 Q8 e0 c( }) S - // 关闭错误报告
' S% Y- u5 }. O' ` - error_reporting(0);
( {7 X2 Q8 W; q; D! R
# P6 R; H7 w4 I1 U- $dbhost = 'localhost:3306'; // mysql服务器主机地址( s u$ P0 i* E% n4 v
- $dbuser = 'root'; // mysql用户名- k( T6 s! x3 c8 a) M! C9 ]' F
- $dbpass = 'root'; // mysql用户名密码3 y2 {+ x+ ]/ E. e# ]4 J
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
! q0 K# R, k6 z. ]/ F* i- g - if(! $conn )" k; c6 O! Y, C7 j% }0 D
- {2 c% ]5 n' Z& G& ]3 f. k) t
- die('连接失败: ' . mysqli_error($conn));
~( r1 n8 X/ t+ g5 M - }' T$ g' O. D5 |& T; {
- // 设置编码,防止中文乱码: |; F4 l# ~& U! I
- mysqli_query($conn , "set names utf8");
* U% I. |, }' U4 \; O - mysqli_select_db( $conn, 'temp' ); & k3 w6 V0 y3 f) O+ a8 L$ g
2 o, l. J! i) n- # php中的文件锁 . ~, j7 V9 ~! i2 t
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 . k' r8 T! u# q4 y O& Y' ]4 L8 l7 v
- flock($fp, LOCK_EX);// 排他锁 4 D; ^( w7 q( N8 Y; a' I
- 2 x' D: @3 S! ~3 S! V# { E1 \+ k
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); ( {$ I- y+ f9 ^/ ^
- while($row = mysqli_fetch_assoc($retval)), q& v) F" y' N
- {
' a6 B# l) p+ Z6 F- y - $id = $row['id'];
8 V& b1 U1 L+ H6 ]2 J, Q - }- h: e/ g8 W) [0 E; d" \
-
2 M9 _0 W+ l8 Y" b0 X - if($id > 0)
4 g. I2 D7 d9 o. U - {
5 u( x! Z8 L6 G& a- v" c- s# q$ c - --$id; 4 f) ~4 U0 B. e ^7 D
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
/ d9 a" U2 Q; a/ k: q5 T - }
% G- t6 s7 a( G5 ]; v - # php的文件锁,释放锁 + Q. G5 p' Y4 B2 J- l1 ~& j
- flock($fp, LOCK_UN);
+ P' e1 S3 s0 F: e6 x! `4 @ - fclose($fp);
8 T, K4 z3 Y4 _3 w( A j$ A* o
$ o5 r1 @! \" F. ~5 c g- // $res = mysqli_query($conn , 'SELECT id FROM ta');
: C/ _0 i/ `8 Y, F$ B: {9 g t - // while($row = mysqli_fetch_assoc($res))3 ~1 p& ~# C+ p) V7 Q( A
- // {4 s1 j- r7 o/ O7 s
- // $id = $row['id'];
5 q& y: C9 K6 u( @7 z' K( X - // }
: M; k) v6 i6 m - // echo $id;
复制代码
0 f8 H& H& G; A% b! A' r2 n% y4 H( H! x) p
抢券活动实例: - public function envelopeSnatching(){/ q4 h9 }. g3 X' w8 p8 B
- $lingqu = $_POST['type'];
1 R' i+ H) ?8 Z# f8 g5 K4 t% @/ E - $uid=session('u_id');//用户id
4 j4 t9 K, ^! O" L4 U4 N - if(!$uid){
2 }1 E. v+ {* P; O: c - $data['msg']='您没登录,请先登录!';+ G/ o7 }. Q$ \! d
- }else if(date('Y-m-d') != '2017-12-12'){3 S( f7 F, y/ _
- $data['msg']='不在活动时间内!';: C: Z1 D8 x2 H* Q/ z3 g
- }else{6 c R. B& N# N" H. P8 l8 A
- $hours=date('H');//当前小时数
, w, J. I* a9 y0 p - if($hours > '09' || $hours > '17'){
3 g0 H2 E% h: s: \5 G+ w. W
5 m4 y/ X9 w- J, c7 _) `1 G! m- if($lingqu == 1 || $lingqu ==2){//点击10点的
; Z% ]2 w ~. n' x5 V( T - if($lingqu == 1){
, J3 b, T4 B0 c' y - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
# ^# {! ~2 E) x" p3 u; w5 U/ p5 G [ - if($hours > '09'){( c8 X$ H% }; W! H, y
- $num=mt_rand(25,28);//优惠券金额0 h y+ W- `$ G' l. [$ s
- $id=1;
' C, @+ V6 B4 ~4 o2 h: F5 i. a6 H - }; G2 t% n6 H; _9 n
- }else if($lingqu == 2){6 J) `2 O8 H1 [5 t" l
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();) B* b \+ F; j5 c/ k( m3 t4 D/ C
- if($hours > '17'){
$ V; n6 u9 v( p% J: d6 f - $num=mt_rand(50,55);//优惠券金额
6 b' s F8 \, j) s' B, T - $id=2;" [+ e. A8 p' S
- }6 u/ u. y. ?) d
- }
- ~& ]9 R0 v6 E* l8 L2 _$ J: h$ ]9 @ - if(!$id){3 D0 p g7 M+ x9 G2 |4 P1 [
- $data['msg']='时间还没到,晚点再来吧。';+ |9 B8 J# \$ K8 P/ }
- }else{0 O7 A2 \6 b! M+ y* U' F
- if($is_lingqu){8 P2 L& p4 u& U
- $data['msg']='你已经领取过了,留点给别人吧!';. w& n/ q6 {- K
- }else{
3 Z( G" X- M. e T - //锁表
8 a& X- r1 n" ^5 e. W. y7 G1 L - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
- h) s" L6 Y( T. A - $active=M('active_num')->where(array('id'=>$id))->find();
* Q& x* S3 m( j) X, u L5 v A* ~: c S& V - if($active > 0){7 H F+ E& d6 @% p) L% e& v, U
- //开启事务
: Q0 { \6 |6 [5 |4 Z4 X0 i - M()->execute('start transaction');+ N4 u5 v5 P% Z) p$ C
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
1 ^9 ~1 c+ g* Q3 J3 S$ ~/ X - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));) A% E6 Y9 d: }- h
- $members_preferential = M('members_preferential');4 o- }& o' P' P# ~: r% d7 P
- //对应投资金额,
4 j% ^8 \: U/ N0 J2 s - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
! r7 O# i; k# ~7 H2 m - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');4 P, _ `* J7 |
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
8 M% C; n5 @2 [2 t - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
' M! R( @4 \; O) B/ t# r. G t - if($save && $add && $add2){3 }6 e2 ~5 n$ w5 ^) J
- //事务提交/ Q8 C( B7 I4 A6 K' J* _ X! q
- M()->execute('commit');% b' \8 v* \2 R* k# F
- $data['msg']='恭喜你领取了'.$num.'元优惠券';. k" M6 U* a& \4 Y, v g2 ]% f' I0 K
- }else{* w9 ?9 l3 f% z* g( [( }8 v
- //回滚0 }% e, u, H8 Z" d
- M()->execute('rollback');
4 V3 B7 ^' Q" w) @+ t0 l - $data['msg']='未知错误!';
- H# t' Z D3 P" g+ w4 x - }/ {! V" k/ r- Q3 t! }$ I Y6 I
- }else{, g9 `$ ~ f1 \- _9 Q# |
- $data['msg']='红包已领完,你来晚了!';
8 c8 |8 w5 a+ H1 D5 H3 B( c - }
" V7 ~( D) z8 k2 y# ?) T4 D% | - M()->execute('UNLOCK TABLES');
) T7 j# u) \* U4 \" ` - }6 U4 ]. |" ]. l* }, V2 n
- } Z1 Z2 L# R" u) p5 t, |
- }else{
6 X4 O1 `3 c& y - $data['msg']='非法操作!';& z5 a4 p0 v; Y4 I. i5 N
- }
3 V: E/ A9 H3 E) y* K) _7 D - }else{7 {5 N: v3 J9 r/ Z: |8 Q
- $data['msg']='还没有到活动时间,请晚点再来哟!!';7 m* F8 I+ X0 y' S3 D* ^
- }! ]# m. \9 k& ?4 }' K) Z6 k: R) J; A
- }
% H/ G l, Y* O6 p7 J" Z - exit(json_encode($data));
. u6 g: h8 v y! i) i% J - }
复制代码
% T* o5 L0 }4 m6 j) B% }* w1 f$ H( L2 e2 ?# j; m' M
|