|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
/ U' D3 }1 h' R' `( ?% DMysql中的锁语法:4 \& R/ u- N6 Q! ]: o+ T
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】& X% V X- M* Y- y, X: j& E9 b
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
% A. M/ R& R, o, SWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
% |8 ]. K: n' S% T注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
# A) j7 j& X l L文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
) c' y8 Y: [$ r7 `) `$ N D测试时,有个文件就行,叫什么名无所谓 总结:: g6 g( W0 D2 x! ^' n u- F
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
5 o& M1 `% i7 M2 r6 M1 ]1. 高并发下单时,减库存量时要加锁
; B9 C' ^) Z. |- ^' y2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
! K" d9 b) n) V! W, K& V- F- J - 模拟秒杀活动-- 商品100件/ N( [) {6 B/ G# W9 D: U
- CREATE TABLE ta
0 ~' z+ ~6 Y4 q" J3 m - (
3 P. Y4 V$ x- O: r9 x- s - id int comment '模拟100件活动商品的数量'
( Z* t: H2 E8 d4 j) U" D5 R4 y - );
& g5 s& Q% Y( V3 Y, v6 x# n% R - INSERT INTO ta VALUES(100);' C# y, X6 u9 L
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件+ l( y( I5 d2 B: w0 }4 `
- */
+ T8 r, b5 X5 B# R( c" P -
- A1 P! f& {) R* z: [ - // 关闭错误报告
8 M$ v" z. {$ {3 o, F - error_reporting(0);
^! w. h9 ^5 w# Y9 V - + I* p6 f$ M, f* W4 `5 P
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
- K C+ G) q, G& _4 {4 C- a - $dbuser = 'root'; // mysql用户名5 d1 Y; y+ t0 T, `5 v J( j
- $dbpass = 'root'; // mysql用户名密码* i3 @1 C; o, F( o8 Z& x
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
0 ?) C- v+ b0 c. Y% r) O5 T9 Z k# o - if(! $conn )
/ L3 v, R2 V8 } - {
/ F0 J, \1 R) ?2 { - die('连接失败: ' . mysqli_error($conn));
. _' w$ ^- q) C3 _" B b* c* h - }& x9 ]" B! K+ g0 e
- // 设置编码,防止中文乱码% Z/ G0 ~' t0 T5 }5 `4 s! C U
- mysqli_query($conn , "set names utf8"); v; M# V- ^( v% O( C
- mysqli_select_db( $conn, 'temp' );
% \% u! x0 D8 }( ] - ! K3 u+ k. U* y6 }! i$ u$ ]
- # mysql 锁
* U1 } G- y# v5 a/ q" f - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
/ P' Z5 F( X, _: G' ~- V - $rs = mysqli_query($conn , 'SELECT id FROM a');
5 G+ o. B* l- J' b P+ ^ - $id = mysqli_result($rs, 0, 0); % W/ L2 `: s0 a) x3 `
- if($id > 0) * v! Q0 R3 k- K
- { # Z; Q# w. u8 S( u/ s g: V" ?
- --$id;
0 P9 m) h1 E; O- z8 i3 u4 K0 D; u- @ - mysqli_query($conn , 'UPDATE a SET id='.$id);
- Q4 F5 j. I7 `- ? - }
( S/ I$ _ B3 }! y0 d
3 |1 n% E' Z. D# g% O, b! l7 _, _1 G- # mysql 解锁 # m6 {/ f/ n/ \: n+ L1 [, e8 h
- mysqli_query($conn , 'UNLOCK TABLES');
; L/ I- ?# l, E; _- I# j4 M) P4 p w - //查询解锁后的id值' G5 z/ h4 C, x1 N; J) R
- // $res = mysqli_query($conn , 'SELECT id FROM ta');2 w0 X+ D1 S3 p* P: G
- // while($row = mysqli_fetch_assoc($res))1 C y3 a. F0 ~
- // {* z6 i5 K$ H' b) w) q6 i. [- ^
- // $id = $row['id'];
/ T+ @6 `8 ]9 E$ v1 n - // }- Q6 R0 v! r9 I+ n8 s3 w
- // echo $id;
复制代码 : n& @4 L4 W! s7 J& o% m7 \" W
5 l3 r3 v+ U C) R/ y3 t; }
+ j) x( U4 v2 }' ]/ I) \PHP文件锁示例: - /*
/ E- A6 f+ {. P8 {8 j: z1 I c! z - 模拟秒杀活动-- 商品100件
) G; d1 x0 L/ l: X! _; `$ s& f- f - CREATE TABLE ta+ r: h# j* O2 p2 @8 U
- (7 S% A {) ]. k! \ L4 [
- id int comment '模拟100件活动商品的数量'5 {- K/ a7 J9 Z8 J
- ); B. t6 w+ w) g3 g: w
- INSERT INTO ta VALUES(100);& ^( u+ f& i1 {+ k- B' w. ^
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
- e3 C) K- y3 s9 ^& i3 W - */ : {4 Y, ]5 D& b% Z- w4 M1 \& v
- $ Y) h) i D( q7 e& I) I _0 @& J
- // 关闭错误报告$ `. Z( Z8 L% w$ W. k+ E' ?0 D/ y
- error_reporting(0); , H: J" u& T; G: Z8 z+ }
* S$ x. Q4 {* f) @& B- $dbhost = 'localhost:3306'; // mysql服务器主机地址/ {; l% I: V! N, o! y1 H7 }
- $dbuser = 'root'; // mysql用户名! X5 {7 a- c' A& j9 a
- $dbpass = 'root'; // mysql用户名密码! g! ^# z8 S) }! @; K( n9 T& [' r
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);+ C, \) z" j4 m+ I
- if(! $conn )
' o, D5 ~* r" K1 i. p% t. [; h - {
! i5 k$ }, G1 g5 D - die('连接失败: ' . mysqli_error($conn));) J" D' M4 a Y; ~$ j
- }
! Z9 h$ k" o$ Z* s+ E - // 设置编码,防止中文乱码
- s. [! y& M: ~- k& H4 W - mysqli_query($conn , "set names utf8");
4 o( e& I0 M, S+ ` - mysqli_select_db( $conn, 'temp' );
& d4 N3 ?6 h) N& x% V
% c7 k: P) P5 L' U* q/ D: j- # php中的文件锁
* \1 t& g- g3 G& u, K# t - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 $ E# p2 }9 ?+ u2 X
- flock($fp, LOCK_EX);// 排他锁 ' g+ Y4 k( M4 K# N" n" f- i0 u
# p/ L8 s8 v; w0 y. |- $retval = mysqli_query($conn ,'SELECT id FROM ta');
8 M' N K! x. C# w- i/ \ - while($row = mysqli_fetch_assoc($retval))
! m1 F, U; V8 l) J; s - {' S: V+ a# \; L% ~6 [: g
- $id = $row['id'];2 j9 R5 `. Y) R
- }0 |- \& K- A- u6 Y7 p4 k+ N
-
5 ^6 r9 A" W4 ?8 U$ {/ [7 A# e - if($id > 0) , U/ d$ L9 q3 l. T; _2 s2 C: [* ]7 n
- { 6 T9 J5 V& Y* ^. V
- --$id;
$ f9 t/ b- g3 G! _. r+ b - mysqli_query($conn ,'UPDATE ta SET id='.$id); 5 d3 j- P* s3 Q2 `* x5 n
- }
+ L2 ?5 @% O1 E- Q - # php的文件锁,释放锁 ! S. o4 G0 F6 g$ Y0 t
- flock($fp, LOCK_UN); & p5 c' o+ D+ Z& A, m
- fclose($fp);
/ D `/ a. g* {% B* m$ i0 A2 l - / @* v' h- |0 K9 w; A
- // $res = mysqli_query($conn , 'SELECT id FROM ta');2 N0 J+ w4 O8 H9 L6 c; N
- // while($row = mysqli_fetch_assoc($res))' r5 z: Y- o0 a% u+ O
- // {
' V2 R4 V7 C' w - // $id = $row['id'];9 U; X% g; V* l9 s) V! V' t
- // }
$ ~* @7 I$ o% p2 o& e2 k+ z7 i - // echo $id;
复制代码 ( K: ~% e+ o5 O, x( Q
9 g% J; }) \( l3 X' |/ t! Y
抢券活动实例: - public function envelopeSnatching(){
7 o6 i( `+ y( x9 t! M- X3 a; K - $lingqu = $_POST['type'];
8 x8 m/ ^& ~8 ~4 j7 N - $uid=session('u_id');//用户id# j7 N- W' t5 k& b& A
- if(!$uid){
4 o! c4 Q( _0 S1 l+ x. M" H! J - $data['msg']='您没登录,请先登录!';, {* u& I% S) g# I
- }else if(date('Y-m-d') != '2017-12-12'){, l' _- _* m6 O
- $data['msg']='不在活动时间内!';
- l- U6 p/ ~# _+ B. f3 c+ M# t6 L+ x - }else{" u6 i! J6 Z; X
- $hours=date('H');//当前小时数
' X% u' m' k4 W- Y% j0 W - if($hours > '09' || $hours > '17'){
: P: [3 o9 h( y
, m% e# ]1 A( I) P+ k# ?' Q7 Z- if($lingqu == 1 || $lingqu ==2){//点击10点的
3 P! @3 p+ k8 l1 y! C - if($lingqu == 1){
* T& E/ t2 s; C+ q. _2 r* w% r - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过7 J8 j2 I) X% \: q/ f8 D/ y, @/ x. b
- if($hours > '09'){
* p* }0 Y$ `' u/ O f# a - $num=mt_rand(25,28);//优惠券金额
9 N M% g Q d - $id=1;) N4 n& ~- M( h8 X1 p
- }1 C( [- B# c- d6 U& b' S
- }else if($lingqu == 2){
% P! T. G5 b% I$ q: J' z3 G - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();3 x) K- W9 c& i! z
- if($hours > '17'){* X. R& Y9 a7 M! J7 [/ _0 a4 r
- $num=mt_rand(50,55);//优惠券金额
8 Z9 y4 _' Y! Z - $id=2;& p6 b+ U, T& ]6 L6 L* U
- }
4 n3 l: u# y2 r0 b* H- ~ - }
1 A h. Z0 |( D - if(!$id){
( B Q# F& {, \ - $data['msg']='时间还没到,晚点再来吧。';* E G8 j$ X7 L" X8 r" j) l
- }else{
* `0 b P2 H2 t5 l8 t - if($is_lingqu){- t0 v7 @) f( n- w" f
- $data['msg']='你已经领取过了,留点给别人吧!';
/ w( M) I; H/ l0 b& n - }else{
$ G1 r7 J7 U( X: w {7 z# n, V! c - //锁表$ Y3 r: v/ ^6 `( W- K5 m6 v
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');/ Y n1 d$ {; Q2 V: f/ Y+ h1 T
- $active=M('active_num')->where(array('id'=>$id))->find();3 l2 G- {$ q. e! K1 A. s
- if($active > 0){" z! U2 `+ T6 b4 D6 t" b
- //开启事务
2 h$ V1 i1 z# l0 K - M()->execute('start transaction');: n1 w# n+ g/ c$ C3 j% c1 i+ r9 \
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
$ V6 _/ R6 {( o, o/ \2 ?: x - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
4 }5 u, t5 R+ d - $members_preferential = M('members_preferential');
' a6 @! h7 j& C! g' M9 i* T - //对应投资金额,
# n1 K2 T4 M* @5 b8 Q' Z - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));$ o1 \: k/ e6 Y& L+ S" o; F
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
0 u: I- l+ g5 S6 K - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
: g4 F2 ~5 T' }+ \) H" b - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
% f& n2 ~% f. ^6 K - if($save && $add && $add2){
% U6 z" z8 H1 v, X3 [3 _% ^ - //事务提交; b/ D% W5 K! F, A( \5 A
- M()->execute('commit');" o; \7 h3 a X m& D7 v
- $data['msg']='恭喜你领取了'.$num.'元优惠券';0 `! h2 u, w) ], J' m3 F
- }else{
' n/ G! ]1 |" E4 x# o k; r5 I% d - //回滚- n: X+ D$ P y& {
- M()->execute('rollback');/ g; w/ S+ s9 r2 |+ t, M( f
- $data['msg']='未知错误!';
/ \+ R; ~/ ?& p - }2 B' z8 M' H( z' x2 R
- }else{
, i" f" e+ r" H) L) _ - $data['msg']='红包已领完,你来晚了!';
+ z$ l! I8 A/ `, s - }, h" r0 U% M, k' K, e1 ~
- M()->execute('UNLOCK TABLES');- v) p' V7 |8 l
- }. ^1 ^8 @1 Z0 t
- }3 T/ X4 M( F, W9 e2 n
- }else{
8 ~# q7 R3 o9 m, N9 D) `+ E - $data['msg']='非法操作!';
! a% s: @" N2 i0 q - }
* Q( `4 K* P! B" q* |/ P- d* } - }else{
0 E8 p, ~& M* N - $data['msg']='还没有到活动时间,请晚点再来哟!!';
) R4 F: V7 O2 C7 t - }
- @2 z! J9 A3 T$ s4 I. j/ f, y - }
6 |" z3 k8 L: Y* {+ v8 L/ d" t - exit(json_encode($data));. L$ n' f2 U6 X# l( t
- }
复制代码
1 C- a% C9 u3 f+ R' x
6 D8 g; X- j' u6 [1 y' c" ` |