|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 0 L, M+ s) F# `4 i" b+ ~' V. ^7 [
Mysql中的锁语法:5 y$ F9 u/ C, V7 N, l1 ^* ~
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】& }; X# w6 q* F/ G
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
: Y5 v8 c" r2 B; i. T$ {/ RWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞- L, ]% v4 `' g
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
- e$ p6 ~# X$ o+ p8 L/ J8 f2 R& w文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。5 q" w, W; H5 j5 f
测试时,有个文件就行,叫什么名无所谓 总结:
/ d" d5 K/ b; _/ M- ~项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:; u" M$ k; p+ k; F: A7 u" T
1. 高并发下单时,减库存量时要加锁
# ]( `/ F1 H# i% R' a: p" v2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
+ Z. M, V: [0 s v2 @' d& a - 模拟秒杀活动-- 商品100件
" b. r8 q' i6 A7 }5 I - CREATE TABLE ta
, [. p' K) J5 b8 d0 Z - (
2 a$ t$ g ~; y# \+ { - id int comment '模拟100件活动商品的数量'
4 l1 r9 C5 f7 ~, }8 g7 M( c+ k" ` - );& z" k! D0 _, A1 {$ c5 R* F
- INSERT INTO ta VALUES(100);
! w$ g' @, G5 Z: |0 O2 V' d - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件% _" h% H4 W; J; Q9 A& I% n, n
- */
, G9 ?) v. |4 i* \% g - , @: X2 ~. G0 c. q4 a5 B4 Z
- // 关闭错误报告
6 B, G. h- R* w) N* H: b - error_reporting(0);
* f% K' C5 ?* a0 C
6 P7 Q. g$ d5 ^3 [% P- $dbhost = 'localhost:3306'; // mysql服务器主机地址
7 G/ U- a4 c2 W0 V5 _9 c - $dbuser = 'root'; // mysql用户名
: w0 l! Q4 ]; M" g4 n) D. `( y; N - $dbpass = 'root'; // mysql用户名密码5 w. k' M0 W4 i5 R) B
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);2 [7 v% Q7 u$ U, j+ K
- if(! $conn )
' F9 O. [9 Q* L/ \+ U - {
2 W+ J0 I2 [% c' h% w. ]# g - die('连接失败: ' . mysqli_error($conn));
( N8 { F% `. R - }, Z0 ^# }% L8 s$ T0 [. G" u
- // 设置编码,防止中文乱码0 j9 l$ Y. i, o2 N3 e% z, O: ]
- mysqli_query($conn , "set names utf8");
( x* R. L3 ?" u: ^; A% B - mysqli_select_db( $conn, 'temp' );
0 Q6 I/ K( F( d8 o2 e$ P" l - ! y! x0 C) j. B4 U% }" v' m
- # mysql 锁
5 l( g6 ?' w9 L2 } - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 ( E$ r; [" u3 y. a( M4 G& f, G3 L
- $rs = mysqli_query($conn , 'SELECT id FROM a');
+ i- F( P5 H1 b8 |2 @ - $id = mysqli_result($rs, 0, 0);
5 ?5 O: t' Q# u: a - if($id > 0) / f8 P# e, x- C8 M( v
- {
6 M/ C1 F& V+ X' I# F - --$id;
& B! Y5 t$ {8 R: P - mysqli_query($conn , 'UPDATE a SET id='.$id);
) q, x1 W, I" m7 i5 Y) X - }
5 |. t* \# s- ]& l, W" E/ t" p - 3 K g; j( c1 w( z
- # mysql 解锁 , w4 F4 p. V2 r1 f- y2 B& x
- mysqli_query($conn , 'UNLOCK TABLES');
* f+ @, W* q9 \: n - //查询解锁后的id值" L+ t2 _8 a* Y6 ?9 w O+ W
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
1 U, R9 V$ z+ d* J% R9 T- ^ - // while($row = mysqli_fetch_assoc($res))
, H2 s* Y" \# p, V' { - // {
" O- z1 o7 ^; `+ x - // $id = $row['id'];
7 b0 S: `0 r7 f - // }
) F" b# v5 o( T7 O - // echo $id;
复制代码 $ P5 ?4 a0 b& R0 ?) [
6 n% P: D( T6 H0 K! [( t+ l
1 s9 y& v: f2 X8 ^- m# H, DPHP文件锁示例: - /*
8 @7 h, D* G# Y1 ^2 m - 模拟秒杀活动-- 商品100件- ~7 i) {' L+ H% h' m5 p/ l* c
- CREATE TABLE ta
( K6 j6 B- K& M" m) [( S - (7 X% I$ W) ?* M5 u
- id int comment '模拟100件活动商品的数量'! R6 C" A V# b) Z9 i
- );
) X! m+ b S3 i& z - INSERT INTO ta VALUES(100);
! w: ~3 C' a1 l9 l - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件$ P9 u9 k" d" G. Y6 ^! @0 c6 ]
- */ 4 `. |2 _+ D+ i7 h
- ( P t) y% E* y2 `
- // 关闭错误报告
- t" {: Y7 S; J8 W: ?' E - error_reporting(0);
0 b% [) H9 _+ ? - ) K5 f! e2 t! | C% M
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
/ Z1 r* `& R* \1 D1 s+ P - $dbuser = 'root'; // mysql用户名
' }6 H( V+ T0 d( a V; } - $dbpass = 'root'; // mysql用户名密码! m1 R$ V; P: l$ e( u1 b {1 t
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
. ~* C/ b& D, E! B1 p - if(! $conn )
0 b m& m! P2 S4 \8 d8 x8 }' R) g - {
8 T! K; r( l" p: ]+ |" D) g - die('连接失败: ' . mysqli_error($conn));
, [; x( W$ {" A5 d9 U- _# j( N - }
8 S5 J* _: p# s, f+ k - // 设置编码,防止中文乱码
+ c1 m9 [" F* E- o! k - mysqli_query($conn , "set names utf8");
/ S! y* }6 L3 @" B1 ]7 Q - mysqli_select_db( $conn, 'temp' ); % k8 L# G% T$ J: y0 x9 C
$ l$ [& h8 ~1 h3 I( T1 a, M$ H1 P- # php中的文件锁
( y4 N- a' @; A* } - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 ) |1 w! G& d2 ^$ ^: K7 B
- flock($fp, LOCK_EX);// 排他锁 / J6 E6 V1 L {0 K7 ^% I
- + Z. r" X) L, o9 a
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); " [' E) l% I) I1 A6 ^- p
- while($row = mysqli_fetch_assoc($retval))) t8 \! s( w% i1 }0 A- e/ N# U
- {" z J/ q( }3 r$ X
- $id = $row['id'];4 k8 R4 G- R' I( G0 R; B9 }$ U9 s
- }8 p3 p+ t s& |2 f c5 s( m3 a
-
3 c+ F+ [0 I6 _% y# s1 u6 @8 A! m' l - if($id > 0) : W1 s9 w/ C' S' b' F, \
- { ' X& t3 ~" I( s* X `, i9 Z, a
- --$id; + C7 ^+ ]5 @) C3 z- O% U
- mysqli_query($conn ,'UPDATE ta SET id='.$id); 8 X4 I; z7 m3 A8 N& S, @3 J
- }
( @6 Y) [( s3 d, [$ b - # php的文件锁,释放锁 ! m D0 ]+ q3 D' {/ n$ J
- flock($fp, LOCK_UN); $ z% Q. P0 b0 Z3 ^, z# L
- fclose($fp);. F7 o* ~; [6 ?* |9 n
5 R3 w# e7 i9 i0 N8 @+ r- // $res = mysqli_query($conn , 'SELECT id FROM ta');5 k4 V- W9 V3 h4 v+ D& S
- // while($row = mysqli_fetch_assoc($res))
[+ E9 | J5 p" g0 @ - // {" n* R$ }; w. Z- ]2 E
- // $id = $row['id'];
3 o V2 [( g8 W. f& b- w+ M/ {, n6 S2 H - // }
0 C9 k3 l- j, E - // echo $id;
复制代码 7 v* R$ r3 `0 L, n
/ R, ]6 H% h; U7 F" p8 t
抢券活动实例: - public function envelopeSnatching(){
: P3 ~6 t4 U, ?2 U6 G4 Y - $lingqu = $_POST['type'];$ X& M3 N2 Z& A6 J. `- w, A
- $uid=session('u_id');//用户id
# M% S# D; {1 ~1 O/ O - if(!$uid){6 o0 G$ R1 ]& N3 `( J6 K$ S/ Q
- $data['msg']='您没登录,请先登录!';) Y# e" b/ S- l! P
- }else if(date('Y-m-d') != '2017-12-12'){
4 e: ?/ G. L% ^6 d6 s# @ - $data['msg']='不在活动时间内!'; G+ Q# }& k' o- K3 f
- }else{* [3 T" Y, q2 v5 n; F
- $hours=date('H');//当前小时数
$ a' H. b" V( \; y; t* b - if($hours > '09' || $hours > '17'){
1 z V9 h+ e, e- y+ } - 9 O3 R( x1 e; Z/ s4 X
- if($lingqu == 1 || $lingqu ==2){//点击10点的
. G" g6 t' y7 `: M$ o - if($lingqu == 1){
+ S% w8 n- W/ L/ n - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过# @: d' M) D6 L5 N+ p4 U- I* `
- if($hours > '09'){
8 t( g% ]3 @, w; B. D - $num=mt_rand(25,28);//优惠券金额
R8 m3 e8 A$ q' x" E - $id=1;
! r m- ?+ E* T V& ] - }
. r; k, [# W4 S0 J& P - }else if($lingqu == 2){
: w D) Z9 [7 i - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();3 A1 |6 c9 v7 ~( {1 \
- if($hours > '17'){
, R" w; D3 g/ E+ l& Y5 R - $num=mt_rand(50,55);//优惠券金额: e1 X. ~. ^3 q$ S j8 z8 H- t
- $id=2;
3 w4 a. U! }& s$ z9 m - }: [; O1 b" m3 e1 e- d$ [- \/ v
- }' L9 E) g) O2 h4 } L
- if(!$id){
, Z; [% r# c" ~! z- S' w8 O - $data['msg']='时间还没到,晚点再来吧。';
e' ?- k5 y% b. C - }else{
9 e! C+ m9 Q8 N - if($is_lingqu){$ \" V5 n/ N) x" x* M# ^8 h- m" }
- $data['msg']='你已经领取过了,留点给别人吧!';
2 r6 ~/ ~: x, L. ^" G3 u - }else{
5 { K/ K. x' N& p9 h l - //锁表
5 o; L- Z/ X4 n5 z2 M" C - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');% z! k% A' c# Z3 I
- $active=M('active_num')->where(array('id'=>$id))->find();' Q6 t4 t9 e) [5 [
- if($active > 0){0 g# s$ m# m+ h3 X( u
- //开启事务 ]0 ], Q& ~/ X' @
- M()->execute('start transaction');3 u8 h$ d, |( X/ i% p/ R$ ?) R
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));4 @5 m& E- P, j) ]1 _
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));; x7 Q$ W/ S" h. V9 C
- $members_preferential = M('members_preferential');
, a7 z3 {% [% F/ e - //对应投资金额,
7 F4 }* K. B% E2 n# Z2 S: Z3 t - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));- T- X V7 c) ` e Z: {
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');- q' @! D2 _2 d5 r
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
3 I/ Y9 V- r! w - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
% E+ m9 f* a Z `4 G& U- K' y/ K - if($save && $add && $add2){
% M! a D5 B) f6 p7 ^- F - //事务提交
; Q7 O7 F5 o/ z' F - M()->execute('commit');
6 F- D' W! _# b( K. v - $data['msg']='恭喜你领取了'.$num.'元优惠券';7 \" |7 Q( v/ D( l- P
- }else{
% f3 _' p" n8 W; M) }0 u% a - //回滚# G, m1 ?7 ]" W0 y/ |6 L1 M4 L4 V" ^
- M()->execute('rollback');2 U4 q& I5 P; h; ^. [" T
- $data['msg']='未知错误!';2 i" a) `( A2 t7 w3 k' L
- }( B8 L4 K# I$ g" n
- }else{7 K( ?( J% m j. ^% {% g2 V9 H
- $data['msg']='红包已领完,你来晚了!';; c, z3 k" C# o9 S Q
- }& V2 T0 {( x- R" U, b& j ~0 X! \
- M()->execute('UNLOCK TABLES');
# C7 }4 s# \% V$ P4 F3 S* V7 N' j+ d$ ~ - }4 s2 w' y+ D; _9 }$ |$ o( V! f2 c
- }( G4 [* ^9 h0 B
- }else{% D' S/ s; g; p: @
- $data['msg']='非法操作!';/ o- p6 h0 r1 [1 r3 E |+ ~
- }9 c9 l5 Z! [1 _6 E
- }else{
6 @/ t& o9 v4 n' A# H9 y - $data['msg']='还没有到活动时间,请晚点再来哟!!';& I4 ~+ |7 b( E; u
- }
. }9 b0 b+ c ^( ^ - }; p$ b6 w# v) [1 C8 x% |
- exit(json_encode($data));0 j- v/ u* f+ L5 L$ a- d8 K
- }
复制代码 : y/ w$ o+ }' e/ P: h+ i$ d2 R
3 m: a& _7 e9 q# d6 a' G8 O9 y |