模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 % n/ i& ~( f5 M! F. \) x" x
Mysql中的锁语法:* s5 }& L# @0 v) b/ m/ h* u
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】# c0 d+ s' k4 b. X; n7 N; A
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
' X- j- {9 r- `. m8 C8 |Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
" g7 Z* D4 M$ j$ D k9 k) l$ \注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
+ L8 L1 {2 Q& I' } m文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
. q5 P7 ~/ X8 D( X7 o! n1 w, h测试时,有个文件就行,叫什么名无所谓 总结:
. Y0 ^& O/ [' J$ k8 k ^ o项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:0 ` z8 L& A& A/ y& G& G
1. 高并发下单时,减库存量时要加锁
3 U/ \7 `* q6 U& |0 \2 d2 t2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*& S( [8 ^- ]( f. m
- 模拟秒杀活动-- 商品100件1 f4 [' o P" C2 b8 I( W, q
- CREATE TABLE ta" m+ c6 W: L- U" C* V
- (2 S. V; [8 |8 P! p( c
- id int comment '模拟100件活动商品的数量'. x" A3 @. Q. E7 p
- );
+ n' D, P- c4 F6 d6 T( z - INSERT INTO ta VALUES(100);
s% x0 Z! S+ e: S) ` - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件" ?7 d, Z% s; b$ O% b1 l4 I
- */ ) i0 D( V+ g8 o; [2 u1 ^
- ! S, H6 Y- f# K# p9 p" K3 p
- // 关闭错误报告
" Q3 t U% k T* h - error_reporting(0); i3 N7 N& v0 _6 _6 m
+ s* e) F& |! @3 v; a- $dbhost = 'localhost:3306'; // mysql服务器主机地址
. `8 X' m; q% M5 |2 s - $dbuser = 'root'; // mysql用户名" ^# ?7 f& f$ P# Q% d) `
- $dbpass = 'root'; // mysql用户名密码
! h4 g7 b. A H - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);. `, e$ t9 L, V6 Z, e
- if(! $conn )
) q4 K% s$ v( N2 a Z0 f) q - {
; c8 {' X% G1 z/ Q - die('连接失败: ' . mysqli_error($conn));( y) F- o0 a! r& P/ a
- }
0 t( O- q9 s2 e' m4 X - // 设置编码,防止中文乱码+ I1 X0 O. z k& b2 R
- mysqli_query($conn , "set names utf8");
! V! ~5 f# @- z, C- C9 r - mysqli_select_db( $conn, 'temp' );
7 j; g8 A( |, U: ~, p2 K2 b( r
. ^0 _. Q( H( u; @, x- # mysql 锁 " m; c s, ]& C% `
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
, _! ~1 e* M+ o i9 U. a - $rs = mysqli_query($conn , 'SELECT id FROM a'); 4 H2 @# y) n* W3 F$ H) M6 o
- $id = mysqli_result($rs, 0, 0); 6 x5 V" C* J3 I& ]& U
- if($id > 0) , `7 c# w0 f* X4 u( F1 q5 O
- {
0 B2 X5 H o; T3 j! U2 y - --$id; % s2 T8 R7 ~' @8 ]
- mysqli_query($conn , 'UPDATE a SET id='.$id);
# w0 L$ J' e- _6 v, |. q - } : R7 l! \6 ]! K' i6 x& ` x8 e: A8 z
- / A5 m, x' C* Z' Z0 T7 u1 f0 R3 i
- # mysql 解锁
2 e( N1 S8 @9 E - mysqli_query($conn , 'UNLOCK TABLES');8 [; ~' u2 t0 R# ^" ~# O
- //查询解锁后的id值
( [) `7 R; c" K( H F1 z3 Z* @ - // $res = mysqli_query($conn , 'SELECT id FROM ta');2 a7 Q! K3 j: _% v2 g& ] B
- // while($row = mysqli_fetch_assoc($res))
' d" _1 m! V, c$ P/ S0 `3 ] - // {
; c. v: P9 Q1 j% F5 v) H. U# J - // $id = $row['id'];4 s9 ] o5 \) D& M
- // }' N! s- T8 I0 i7 H. p* J+ X
- // echo $id;
复制代码 3 ?/ B1 }4 i' O: a( X' V
R6 R# s' K p4 Q9 K% g- G" e
: G3 H3 R/ m" c* \$ W# m/ GPHP文件锁示例: - /*
/ l- x) \% J$ z! h$ f0 v7 u1 }: L! I - 模拟秒杀活动-- 商品100件2 j1 F" m1 X+ O( p8 @8 F( l
- CREATE TABLE ta* N" Z% A9 b4 t; [
- (
$ v) C) M, _6 V- T; Q - id int comment '模拟100件活动商品的数量'8 F1 ^& e0 X' P* p* \
- );
) P. c' e# q- S9 u0 I% ^( d5 Z - INSERT INTO ta VALUES(100);) d* i$ W; O5 ]! N. [: C
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
* G! b+ B* t" y5 Q - */ " U- X7 f% Y( ^; o
- - t7 G$ t0 k$ ?6 d) u
- // 关闭错误报告
+ a$ K+ W0 m) v/ o) A% v; t - error_reporting(0);
% H0 h! o8 T$ B: S - , o; ~# k0 v7 V8 k2 s n4 |/ n
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
4 b, h4 H6 x( l; q5 S - $dbuser = 'root'; // mysql用户名
5 u) R7 \/ v# J: I* q9 K - $dbpass = 'root'; // mysql用户名密码9 V( ]2 E1 C0 }. v+ ?3 t& w
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);6 H* g% ^ f: p" U9 m
- if(! $conn )
) l+ V* `& |3 K$ J, [: m3 p8 F - {
/ P' @- X0 o* M9 G1 @1 F6 Z1 o - die('连接失败: ' . mysqli_error($conn));. W% U, T. U# R$ o
- }
: o8 e, ~! X, V" B - // 设置编码,防止中文乱码
8 O p$ a" q, I+ E - mysqli_query($conn , "set names utf8");
5 n: Z( k3 {9 V9 U; P. b8 v2 l3 m" r - mysqli_select_db( $conn, 'temp' ); 5 R! ~/ z1 X1 Z" O% M! T
$ S# [: j) g5 m, o, R3 o: ^/ P- # php中的文件锁 % v3 L5 G _" A& H+ U. q
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 - o3 V& ~9 X$ B5 u
- flock($fp, LOCK_EX);// 排他锁
% P+ W2 g" r3 V; r. L. }7 Q) ~6 T+ H
m9 W+ Y1 n: m% u# V7 Z, B- $retval = mysqli_query($conn ,'SELECT id FROM ta'); % N* I7 J% B1 l) N& e0 P% t/ ]! N
- while($row = mysqli_fetch_assoc($retval))
; E- ^( o' b7 l. b9 O$ c: t7 R7 X - {- t2 s( ~2 G+ S+ U3 n
- $id = $row['id'];% G' X* L$ \1 w6 h
- }
; X+ W$ G- A; q, c% T -
& q O% _6 V) Q - if($id > 0)
' F9 S* z. A& e' W4 G/ H - {
* [3 J, d7 ?3 n - --$id; : n; {1 Q) p# f
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
, ]) d+ L0 y2 q2 V* v2 m( p2 V - }
* t, r4 a$ t- E" @4 G& g, z4 t - # php的文件锁,释放锁
/ n! X+ L. M7 m s - flock($fp, LOCK_UN);
6 x5 |4 l! ~, B7 }/ O! d2 B2 l - fclose($fp);
f6 M8 F- V0 ?
; Z8 r3 ]8 \ c- // $res = mysqli_query($conn , 'SELECT id FROM ta');
5 e* H/ R' n# A9 F - // while($row = mysqli_fetch_assoc($res))9 E" }7 ^) b& |! V0 M; W
- // {
) e% p7 _' x* c2 s6 |8 P$ K9 ` - // $id = $row['id'];/ o# K2 \! i: M3 q' F+ I1 @
- // }) C1 K* Y/ |! S9 T: }
- // echo $id;
复制代码 7 }1 I5 I) E' `& b# m
2 v, f& Y( _& [, @8 l v% `
抢券活动实例: - public function envelopeSnatching(){
) `9 d# x/ }& o. G- r! P0 f - $lingqu = $_POST['type'];
) W7 O( n4 v5 D1 y - $uid=session('u_id');//用户id; _# V& }4 f) J( P" n( M
- if(!$uid){& c; N' I3 E) Z8 j0 y+ ?
- $data['msg']='您没登录,请先登录!';- L& [- u+ }; T" G! v3 L# ?+ S
- }else if(date('Y-m-d') != '2017-12-12'){$ m- `8 e$ b) r3 o1 k( h
- $data['msg']='不在活动时间内!';
! F# ~+ D- q/ z' ~$ @9 Q; @ - }else{
) [' p. ~% \% s" Q/ f. K8 D - $hours=date('H');//当前小时数
6 J" b% q: S0 G6 L8 a - if($hours > '09' || $hours > '17'){
! }% ?2 g1 l7 N/ u - # t( z* M8 S6 Q2 S" l
- if($lingqu == 1 || $lingqu ==2){//点击10点的8 A$ W% Y ?8 I6 v
- if($lingqu == 1){
5 T: v. G% |4 q, ` - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过" }* K& B! x9 H
- if($hours > '09'){
3 `1 f) I4 Z. Y! s+ P - $num=mt_rand(25,28);//优惠券金额$ ^% P3 [! q( J! k
- $id=1;
! e+ _9 z! @, r/ I - }
! V; G7 m/ z; S - }else if($lingqu == 2){6 ]* Q, Y6 X6 g
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();5 B9 B% p9 P8 _+ [/ ?/ v7 I8 r
- if($hours > '17'){2 E1 l6 Y8 h m5 c7 r5 \
- $num=mt_rand(50,55);//优惠券金额) M" x# A ]0 P: H
- $id=2;
5 i* v3 b0 h1 h - }, j- @" b8 H" f7 G
- }
4 ]( A% m; |/ R2 O. `2 Z' `3 d - if(!$id){; r$ Z' O+ L& o
- $data['msg']='时间还没到,晚点再来吧。';
6 X* Z5 B; w2 [/ n) F* x - }else{
5 W; o0 {! \! f6 A - if($is_lingqu){
6 c0 S9 ]9 q% J6 I( j+ A4 f - $data['msg']='你已经领取过了,留点给别人吧!';
& M) p5 Z) l* x, C$ m j - }else{4 o% [$ U# s0 s" T3 T$ F6 ~
- //锁表
6 P0 U/ r# K. z: e - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
3 X9 Q6 f6 y6 `0 K8 S: \+ ~: y - $active=M('active_num')->where(array('id'=>$id))->find();& s$ Q4 N" r, i( {4 {+ C' `
- if($active > 0){6 {, U Z& O1 {. O) V/ i1 O
- //开启事务7 U% Q% S7 A$ O4 y
- M()->execute('start transaction');( o9 V# |& V+ U, ?$ k8 i/ b
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));9 z# ]9 X% t2 ~( @( W( ~ Y
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
; L) y6 @4 H) {% `; V - $members_preferential = M('members_preferential');3 {7 b% t ~ q1 l6 |7 y
- //对应投资金额,( b$ q2 x5 ?& q6 d9 X$ ~4 \
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
4 w p0 a; |/ J, `! i/ g% R, E - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
; A( m% N7 Q. i. `* v - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
5 z: x( x5 y1 c5 Z$ }1 [3 u! w - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券. [) O4 h _$ C3 ~( U1 [$ ]5 A2 H
- if($save && $add && $add2){
6 F9 C$ [# v. N4 E7 p: j - //事务提交
: c( `) L2 y9 A" P) B - M()->execute('commit');
1 Y$ C1 ^7 p- ~( G) L/ S( U5 ~2 D - $data['msg']='恭喜你领取了'.$num.'元优惠券';
/ ?) y0 j0 h$ w0 W7 m; k$ I - }else{' ^4 G5 N& e$ O2 g3 K+ x# W
- //回滚# }$ g2 E |! e$ h" Q: R ?5 Y
- M()->execute('rollback');' ~+ u; r6 e2 v& ]
- $data['msg']='未知错误!';
/ }2 d3 v) V8 C7 H! P+ ` - }! s; I( c C6 u% u4 c
- }else{/ P2 u. A$ h! l% S2 {- \) |
- $data['msg']='红包已领完,你来晚了!';" j5 g7 r5 ^& ]: P; K/ c7 L% \; y
- }2 X: K0 `1 h+ a, [
- M()->execute('UNLOCK TABLES');
. g! G7 |$ ~0 z1 X: Z - }" R, D$ F! J, n/ y9 y
- }5 O5 b8 k( ~' V' E1 o# w9 i' f/ T
- }else{; c" U% ]2 B A
- $data['msg']='非法操作!';
0 T( k Y, p4 h7 e1 q: R - }$ l9 h3 A* S( V; w( o
- }else{+ ~* p2 E. {9 ~( h$ S
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
( Y) u1 K4 p! R$ E; S+ B. W! ~ - }
4 w: @$ C! |0 K7 J. _ - }
7 b& ?2 F4 Z$ e3 V& ] - exit(json_encode($data));
) i7 I3 ^2 I9 r5 }' C - }
复制代码 6 F1 N1 d. T: E2 L* f# {( r
- |3 t' H4 v, U+ ~3 c
|