模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
9 J7 O+ K1 \! q1 OMysql中的锁语法:# L3 f* F! C& {) T) y$ ]8 m
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】% l! }) D3 |( M- n9 [% R
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表7 V) `8 t A5 j. [6 I6 I
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞9 b) w- i0 S% b0 R
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :( A5 u% O+ d; m1 c. _, ]
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
: D+ e+ P I' F: Q* x测试时,有个文件就行,叫什么名无所谓 总结:
( l/ Q( p7 `2 Q- s* @项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
) g/ U. J8 N- j3 c# m, U1. 高并发下单时,减库存量时要加锁& x# ^) ?: G* W) r3 P
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*: @; T9 z' _$ y
- 模拟秒杀活动-- 商品100件
% A' t* {2 P6 v- } - CREATE TABLE ta
6 ]" Y5 x; F9 X% I7 e& J; J6 o3 H - (
+ P A3 Y$ L+ G- Z, X - id int comment '模拟100件活动商品的数量'/ M- f& b+ R9 v+ w6 {( Q# x4 B
- );
. L' {$ y7 G9 h; H - INSERT INTO ta VALUES(100);
6 L ^: i# z% r$ Z& v( F# r - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件' ]! h6 v2 i @# }7 p4 S2 {
- */
# }8 V0 R. c% ?8 t' F - 0 c6 u( x* }* d s1 `+ G% ]6 v
- // 关闭错误报告1 s8 A7 U; H8 ~: w
- error_reporting(0); ( v) l4 M9 e, V5 N3 o4 y
- 2 ~! n) [ B# g o: Q* T8 x
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
5 F- K; O7 x, t8 S. r9 ~; ?9 j/ o9 e+ e - $dbuser = 'root'; // mysql用户名$ H# t0 N5 \& V, A" D+ v: R: u! s5 V& e
- $dbpass = 'root'; // mysql用户名密码4 T4 w5 A5 s8 l, V4 a" S+ r7 m
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
5 E0 z% L1 n" [4 s3 w5 Y) y - if(! $conn )
' `( C7 \4 Q' O' ^6 g - {
' F6 a2 H$ s8 c8 c" x2 { - die('连接失败: ' . mysqli_error($conn));
) g% i1 Y1 e) \& x7 g7 T - }$ f$ L9 D. `4 w: Y( C; h/ u0 f
- // 设置编码,防止中文乱码& S/ W- C! o" L: E& d. D/ |9 N; Z
- mysqli_query($conn , "set names utf8");
; f, }+ j: V; p1 {# o& C - mysqli_select_db( $conn, 'temp' );
5 @; X3 O8 G1 l3 ~8 ^: ]- E - ' x: o# F8 H4 V, F; U3 L" F
- # mysql 锁 " M6 v, a' H4 G
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
8 V) K6 g. @3 K, R - $rs = mysqli_query($conn , 'SELECT id FROM a');
: V* Y: i; ^& k+ o' Z* n6 W - $id = mysqli_result($rs, 0, 0);
3 n$ G, u: q: e# l1 A: U4 }* P4 k3 [ - if($id > 0) 3 p ]) y$ B( s5 H, c
- { ) [4 Q3 m4 g P) O& W* [
- --$id;
8 _# \* K% `6 n1 t6 y0 s - mysqli_query($conn , 'UPDATE a SET id='.$id);
. h6 S0 P8 d% u7 N1 Q$ i0 }& E - } 2 _* d8 v$ x8 G% }
- 9 ?. M6 p a: R3 N
- # mysql 解锁
. y7 J( v) }+ u# t: `' \! H5 I* h - mysqli_query($conn , 'UNLOCK TABLES');
; K1 F* Y& g9 F( M4 {- B3 `& d - //查询解锁后的id值
2 x5 p! R+ r9 m( Y - // $res = mysqli_query($conn , 'SELECT id FROM ta');% d; e( s y. Q! p- ^# z. b% p6 e
- // while($row = mysqli_fetch_assoc($res))5 D9 }; t: q# T6 s4 b5 f
- // {
! u" b* g0 f- L- G, M, O - // $id = $row['id'];
+ c. X! B0 O: c - // }$ r- f% D4 Z8 P1 x1 G
- // echo $id;
复制代码
4 Y( V( @- b3 _! M$ ?! a/ q( Y" ^6 M$ \( q8 ?4 T% a; X4 C
! t5 [$ _' k( K/ c0 y
PHP文件锁示例: - /*3 O- t5 @# n; O. s0 M$ i
- 模拟秒杀活动-- 商品100件
& [2 q2 p! Z. Q. F - CREATE TABLE ta
+ ~5 ?) \7 J0 N - (0 g% j! [, Q# E- J- L* r; }3 _9 I
- id int comment '模拟100件活动商品的数量'7 y, Z! T! `) V8 G5 |8 z" E
- );
9 D# k' q* n+ R, F3 r+ S) N* e - INSERT INTO ta VALUES(100);. [/ O* s \3 w- n5 G0 i* ?
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件$ ?$ a( y& k0 c5 T
- */ " N8 \9 m) P* ?5 ]
-
3 U6 E: L& k# v. g; ?9 X - // 关闭错误报告' c( w' r" @, J4 z: p$ ?$ ?
- error_reporting(0);
& p1 V9 t- v. G* f - " I/ n( U+ d4 L+ |
- $dbhost = 'localhost:3306'; // mysql服务器主机地址. \9 r- O2 Z0 W0 t5 M) X" Z
- $dbuser = 'root'; // mysql用户名' b2 O0 j( W2 ], b% o% ?! Y/ W
- $dbpass = 'root'; // mysql用户名密码
4 {) u: D v- b A, x - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
( P _+ L' O4 K( N0 [2 q4 n) D - if(! $conn )
* W+ {6 i% R( O% _7 ]; O" i - {# L( M/ _. G( ~( k9 S) o
- die('连接失败: ' . mysqli_error($conn));) N6 A! Q5 o: l; g* W
- }# d! W3 t* I/ x% @ L$ k: g
- // 设置编码,防止中文乱码
( w8 f4 S- ?" F; k: H/ r2 Z3 ?2 v - mysqli_query($conn , "set names utf8");: B$ B1 Z( P1 |4 N
- mysqli_select_db( $conn, 'temp' );
$ q% ]4 D) ]8 g+ t1 |* V - 5 B3 M$ P" I- [$ @' y8 b5 |& g, M
- # php中的文件锁
( e: l7 n7 o& n. Z - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 ' O7 u4 h W+ c% s" _/ @% W
- flock($fp, LOCK_EX);// 排他锁
- z5 ?& d' s7 Z/ R - 7 t3 r e9 C' Q9 O' e8 f7 Z0 |$ Y
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
8 f* C, {, F* z* r6 E- c - while($row = mysqli_fetch_assoc($retval))
3 J; q D+ j) }" |3 _+ Q, @ - {
- t% ?/ Z) F/ i7 W0 [( v& E - $id = $row['id'];
- x* R, E/ b% O6 B" R - }
2 M6 P& w0 Y! S9 R" k. } - ( k6 c4 P: K5 u5 V0 }
- if($id > 0) 7 N7 Y- e0 x0 ?! Q. h$ g
- {
. ]4 ]8 S( h8 I9 V* O - --$id; 0 `; n" @& W7 ]8 l0 y; S7 r
- mysqli_query($conn ,'UPDATE ta SET id='.$id); * ?2 ^ _! m: Y
- }
5 V G' C4 q s, p' i$ { - # php的文件锁,释放锁
1 _2 [( x) b* [. \" c6 o - flock($fp, LOCK_UN);
! v- M, P" _, G7 V' P - fclose($fp);) w! I1 v `8 [, N
- ; b: }) M, J. v3 e: I0 k- y
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
9 u, j& `8 j; ]$ p. C7 r - // while($row = mysqli_fetch_assoc($res))
1 D5 _% F# G% i- ^: c, c8 q' R4 V - // {
* Q! L' u) i% U! n- M% U/ u, E - // $id = $row['id'];2 i7 `; C/ Z0 U) d5 g# s. h# I# R
- // }8 W6 P/ ? h/ U# o2 Z: V6 T
- // echo $id;
复制代码
* \& A- r8 K+ Q5 S7 _0 T. x7 x; M6 |/ W: P/ x8 i
抢券活动实例: - public function envelopeSnatching(){
- T. ~3 t0 H* K1 S: S0 l - $lingqu = $_POST['type'];
2 t, A/ c) r n( b$ z8 _/ Y - $uid=session('u_id');//用户id! _ Q l2 Q1 h8 ], ]
- if(!$uid){+ o M4 r3 Z( o5 L9 g4 V! O: R
- $data['msg']='您没登录,请先登录!';. z0 [: c; X. y0 k
- }else if(date('Y-m-d') != '2017-12-12'){0 m8 j. O) ?* `! u$ m0 T
- $data['msg']='不在活动时间内!';
- C/ [8 s. j; y) g/ W. n - }else{
9 S- I, Z* w; P# V, {) A% ` - $hours=date('H');//当前小时数
6 I/ I1 x" m* U: a' p - if($hours > '09' || $hours > '17'){
5 d+ {4 p y, g2 j6 Z M
* R' M: s: B+ Y+ T- if($lingqu == 1 || $lingqu ==2){//点击10点的7 u: x4 S8 `$ J8 w+ Z- H0 h9 ?
- if($lingqu == 1){
$ J8 W! o; \$ q; f/ s& m7 @6 y7 e - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
7 M# G% E" ~) p! Y+ b - if($hours > '09'){
" W, m9 b6 u( i5 R9 Y6 z+ i ? - $num=mt_rand(25,28);//优惠券金额
. N# Q& A. }0 v3 j6 j) ]+ O - $id=1;9 u+ R4 a Y; Z, A5 N3 p4 w
- }
6 s, r" [& M: p7 _& { - }else if($lingqu == 2){: T0 Q! k Q; x% N
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
+ P- k8 C- K5 f( ]' }2 _* I- i( w - if($hours > '17'){
: \/ T+ [% f' `/ j - $num=mt_rand(50,55);//优惠券金额% }; e9 r7 @. s0 z* {' `
- $id=2;
2 @4 ?$ G+ D. {7 n/ f: h( H - }( W8 J2 ] Q' M1 m6 {* v
- }
" H" t$ P7 E w. y9 R - if(!$id){
5 {2 v! b1 h+ d \! f1 c- B - $data['msg']='时间还没到,晚点再来吧。';0 F$ w1 n0 C+ @1 E* c
- }else{, u( l/ T" v! W# G8 U/ o
- if($is_lingqu){
- ^& v M2 L% a - $data['msg']='你已经领取过了,留点给别人吧!';
: M) G! z" L- G/ m - }else{; R) {5 E( b' A; L- P% h' o
- //锁表/ c! B& I0 z" w1 h% J6 K. i
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
- s* _2 @. P; Z# J( n' K - $active=M('active_num')->where(array('id'=>$id))->find();
1 D. V. C1 P% W1 ?& w* Y$ [8 T - if($active > 0){6 [& @3 L" g' s* l
- //开启事务- @2 b0 }# F' g) M5 r0 E* ?" K. I
- M()->execute('start transaction');! v' H8 e" I) b: J+ N9 R
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
# x1 {* V: Z' G+ h4 Z - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
4 w9 O4 d3 [6 [5 [* t - $members_preferential = M('members_preferential');+ `' e, T* |* e: F9 D- G5 G k
- //对应投资金额,4 P6 u- I! _" X" I& c4 L
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
6 t* s" N- l( i - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');" [* C: w+ c& \ c9 O6 b8 p
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间- {% G. K2 A. w8 X8 N
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
! R9 L; {% u5 U" f! ~+ { - if($save && $add && $add2){
3 z" x7 E, \: k7 P" s: X+ R" L - //事务提交
, G/ R! y" c. T, M. j - M()->execute('commit');
& m# Z. M: _1 v3 ]# z% c - $data['msg']='恭喜你领取了'.$num.'元优惠券';
/ y3 I' t5 K, Z0 ` - }else{! Z7 M8 {6 I8 i8 w
- //回滚! F' k# {# e% q
- M()->execute('rollback');% b! x1 E* f. j; ?2 [
- $data['msg']='未知错误!';
3 A1 Q$ s/ z1 {; j& h6 } - }
3 V7 p! D( J3 M* Q- G" r- F8 K - }else{
' h& v$ g( G6 t0 W" K5 y# J& V$ ]7 V - $data['msg']='红包已领完,你来晚了!';& ?! W* j/ r8 E' u, [
- }
+ n; _; x) I8 ?2 s - M()->execute('UNLOCK TABLES');
1 `& p9 b: s2 e( C' r p - }
6 ~- }; t5 q! K2 F - }0 a+ X# W1 ?9 O4 t7 L5 x
- }else{
" E) o8 z v& A# |1 r/ b y - $data['msg']='非法操作!';9 U1 {7 `- ~7 |( L4 [4 T k
- }4 \# K, W: K2 C E
- }else{
; t$ P, H$ c# b# m* C ? - $data['msg']='还没有到活动时间,请晚点再来哟!!';) H$ D6 x# i0 G9 ^ c0 @& Y
- }3 j" L) l, A& @6 {
- }2 x% ?3 ?: Z5 n( f
- exit(json_encode($data));( a! p% \1 w; Q0 J f
- }
复制代码 4 g5 P& ]: Q4 w$ y; j- i
5 w, R' R/ d% d) p1 i. r |