|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 4 S V4 a# C5 A( q7 z/ H" E
Mysql中的锁语法:8 n0 u( @8 e! B( Y9 {
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】+ o4 I& q% j8 d
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
5 `" U$ Z, _5 _. M6 ^! VWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞4 D* P; {' Y2 B! n2 t! O
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :: B! X: l4 G8 G2 g. [
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
8 ~. h# `' d% s1 S测试时,有个文件就行,叫什么名无所谓 总结:( x$ q7 ~$ r! o8 c/ ]
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
5 d8 L" z/ t, U+ R1. 高并发下单时,减库存量时要加锁/ v6 t& \9 d! e+ o/ r
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /* S. ~+ G% v' ~
- 模拟秒杀活动-- 商品100件2 u [1 D/ e3 B5 ~( r
- CREATE TABLE ta
6 K3 z+ i4 k# { M* G2 f - (
$ G" K5 T" Y5 A" _ - id int comment '模拟100件活动商品的数量'
( m2 o2 \9 L" H! L: ?; v - );4 K7 c( H8 ?" ^8 o' @
- INSERT INTO ta VALUES(100);
, {7 d( c! o% X. F' h - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件) i0 y/ o* e/ z) z! J7 ?0 R! }
- */
+ T- v8 f9 ]" v, Z! k7 D - $ J5 k8 e; J$ s+ C; m
- // 关闭错误报告4 X( [0 w9 K4 u7 C
- error_reporting(0);
5 F8 O' S0 o, m! f2 s _: i
# f2 h* ~! u& [7 F6 c/ G- $dbhost = 'localhost:3306'; // mysql服务器主机地址- ?7 X& |/ G: B. I" O8 u
- $dbuser = 'root'; // mysql用户名% L9 l/ q7 E8 R% y
- $dbpass = 'root'; // mysql用户名密码/ u6 g9 T" k- y
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
' t7 T' g$ k, b/ r0 d - if(! $conn ) y/ Y, g. M0 Q& Z9 b1 d! U; o a; i6 Z
- {6 f1 p* ^8 y0 g+ w4 X% W/ L: P
- die('连接失败: ' . mysqli_error($conn));1 j2 V, B- r, b0 w: p. @. Q/ r4 f
- }
7 Z# _7 K1 A' |" _, p. a - // 设置编码,防止中文乱码
% o* n f2 w: `$ t- ` - mysqli_query($conn , "set names utf8");
. r2 m- d' J4 a5 K" z ~$ W! H - mysqli_select_db( $conn, 'temp' ); & o+ p, B3 L* C! k" y% E) m: v
5 S2 x' u4 H3 Z" d, K5 v1 X, I- # mysql 锁
% D3 E I6 Q$ g7 F5 a - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 1 T( d7 a7 p" U) Y' M& o4 R: j
- $rs = mysqli_query($conn , 'SELECT id FROM a');
) z' ]$ @. j/ D - $id = mysqli_result($rs, 0, 0);
& E& U2 O2 Y* V2 c9 u" c% U - if($id > 0)
% z4 h; Y% Y1 q& z/ Z9 Q - {
9 a9 V X5 V) p- R - --$id; : h2 f' f# h" y0 `! \5 j' q
- mysqli_query($conn , 'UPDATE a SET id='.$id); / @& ^1 }. ?2 c" S4 ~& ?, P
- }
4 X8 U" X& p. Z4 z0 I4 O6 s4 P6 c* e - 1 C4 Z: T2 }& Z/ K+ S3 F/ q4 v
- # mysql 解锁 % }$ X( y& Z+ {$ y1 Q
- mysqli_query($conn , 'UNLOCK TABLES');
8 Y4 r, K* R+ F6 e! ] - //查询解锁后的id值- ^5 D* a. l$ x h' r, d+ |7 ^
- // $res = mysqli_query($conn , 'SELECT id FROM ta');/ `' d# H% C. w4 N1 b" l+ [
- // while($row = mysqli_fetch_assoc($res))( j R% f/ D3 ]0 `
- // {
' ^7 _- u& z% l8 }1 W, ` - // $id = $row['id'];, I) q o1 Y" ~ I6 ^
- // }
! |# Z; Z, c) K3 U3 F& X - // echo $id;
复制代码
" r' F5 }2 D8 N c
* o% ~! h+ X, x9 G9 O) p( t( w' |* Q( v( \ y& Y
PHP文件锁示例: - /*
( x" ^$ A5 Z' m5 _1 w9 N+ k! \7 t - 模拟秒杀活动-- 商品100件* |3 b2 K( R0 w: n0 y& H' Q
- CREATE TABLE ta" M- Q0 @+ _. c6 z$ v0 N8 |, M
- (2 ~3 m/ Q5 ]; E6 e2 c; V8 p: e
- id int comment '模拟100件活动商品的数量'8 O1 x: t! ~$ E' }
- );
|9 e2 t0 i( m, ? - INSERT INTO ta VALUES(100);
8 ~! @% @+ U/ f2 Z0 U+ ?2 p - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件+ q7 m3 f8 `, X4 w
- */ * X' J) O: u, a
- 2 U+ V7 {& }; @ w* j& P( Q
- // 关闭错误报告: N2 e2 k/ z+ E
- error_reporting(0);
8 c+ ^ h. w6 z" D2 ~0 ?! l - # W$ t# J2 O# Q( k
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
. h, ^) M9 _7 p, D; [ - $dbuser = 'root'; // mysql用户名
- B. ?- l3 o$ Z' F# G - $dbpass = 'root'; // mysql用户名密码
0 w# P/ t0 M/ D$ n# m7 F - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
3 p: z3 h) N, ~8 [2 Z - if(! $conn )! T8 c$ W) p5 K- m. G& C# a0 d
- {1 Q. ^7 H1 r' r, a* R
- die('连接失败: ' . mysqli_error($conn));2 p* C$ l' O" X7 z( }
- }
" i4 |8 Y: m/ C: h& o- c - // 设置编码,防止中文乱码6 R; j6 c7 w! T: b9 O0 g0 n9 ]
- mysqli_query($conn , "set names utf8");) d S( k! Y+ h2 Z8 j0 [
- mysqli_select_db( $conn, 'temp' );
5 T! K) ]8 k4 W+ U& m) q
! p0 e" e: Y( Y) {- # php中的文件锁 # O& o# y' P+ H* ]& R
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 * F3 ~( c( p, P* m+ }- Z
- flock($fp, LOCK_EX);// 排他锁
: s& X+ N& H! X
5 @" v9 d k, B& R- $retval = mysqli_query($conn ,'SELECT id FROM ta'); & h( i9 s1 H/ }' K% u( k) l U0 V5 g
- while($row = mysqli_fetch_assoc($retval))) D( D) G. F4 [- a" Z
- {
) V, H ]9 x- v8 S6 ^9 j2 P- s - $id = $row['id'];! j$ f9 n' r6 Y2 I2 J. ~- y
- }
' z) @. ?$ n& [ -
9 \" S# P7 T2 o S+ j3 j2 A5 W - if($id > 0) % \% u3 u# {8 w/ S3 R" i' G
- {
6 d7 N* n$ x' L6 _ - --$id; ! _+ K. ]* K( E+ D2 G# r, r: g5 ~
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
$ y4 R* P- N3 T - }
) i0 B' r/ V- V; m - # php的文件锁,释放锁
0 ^* J" C' e' Y8 q, G+ @, H/ D' K - flock($fp, LOCK_UN);
; q8 j+ T& q0 `( ` k) L7 _8 h - fclose($fp);- F" S6 F4 I8 l. o
! E* r$ [6 E- E% }6 V' w# l5 x1 G- // $res = mysqli_query($conn , 'SELECT id FROM ta');
! H" r) R4 ?8 B- v - // while($row = mysqli_fetch_assoc($res))
# V1 _! A* k- K5 _ - // {
5 U& ~: u& O L7 E - // $id = $row['id'];+ s: s) I4 U6 T& [6 u+ v
- // }
. M- h4 I) p4 O - // echo $id;
复制代码 + Z9 t& q' v/ g0 M, Q+ l$ W
9 _* I5 L: x) K3 `9 O
抢券活动实例: - public function envelopeSnatching(){
+ F# D( F9 ~! @0 u# q$ t - $lingqu = $_POST['type'];
% l& i3 K+ Q" T' l6 X - $uid=session('u_id');//用户id# f# R% p4 I+ ]8 u; ?! B3 o* b
- if(!$uid){) i, y9 Z* j& r4 g/ ^2 `$ K
- $data['msg']='您没登录,请先登录!';
: @( O1 m- X i6 T) o! [ - }else if(date('Y-m-d') != '2017-12-12'){
* Y% b; P5 A0 h# T, J c/ a - $data['msg']='不在活动时间内!';
- ^+ r7 t& x5 v$ O; D - }else{
2 L7 j1 ]4 U" f d1 d, U j - $hours=date('H');//当前小时数 W% H: v3 @2 t8 y
- if($hours > '09' || $hours > '17'){
% W8 V( m2 g: |8 [
, R, \5 o! g5 i9 A- if($lingqu == 1 || $lingqu ==2){//点击10点的
! r' l& P' Z3 k- m1 m1 c9 r9 L - if($lingqu == 1){
3 |3 X$ ` h/ w& D8 [ - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
0 ]7 O2 v& g$ z: K1 d* `7 w O+ J - if($hours > '09'){
" R8 }5 ?" }1 |2 `: D - $num=mt_rand(25,28);//优惠券金额& h1 `8 n" T+ K0 e
- $id=1;& Y) d: S* N/ ~" R0 q2 q( v
- }* H4 V/ q+ \- b5 z2 h
- }else if($lingqu == 2){ [' f8 r. n& J7 U
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();& T5 [$ E* U+ J. i
- if($hours > '17'){
! Q+ }, o. g3 }5 E, `: t1 T: I - $num=mt_rand(50,55);//优惠券金额
# s: ^# C' Z, S. \& o) ^( a. x - $id=2;
; s8 g# v2 z9 k9 s; b+ K - }9 N. Q5 {4 o$ ^ S" J. }9 }
- }9 S/ C8 R/ E7 B0 K% F6 Y, m
- if(!$id){) x3 G! k8 a$ i! F$ E3 r& q) `
- $data['msg']='时间还没到,晚点再来吧。';3 g% T: d% `7 x
- }else{% N$ Q' v/ ?4 s# M1 M1 C6 n
- if($is_lingqu){9 t& p6 @$ [6 e
- $data['msg']='你已经领取过了,留点给别人吧!';
5 {2 {# {) A3 P( T! V+ M - }else{, m v* t* c2 C2 O( \
- //锁表
# E# ?2 V: F( X6 c - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
1 N8 r$ O' U, X, Z4 _2 | P - $active=M('active_num')->where(array('id'=>$id))->find();
4 ~, ]: _8 {8 u& p! n - if($active > 0){
/ t; ^" i. |4 q - //开启事务
6 x. g% S! B/ P7 ? - M()->execute('start transaction');
9 }) O) b. [ M2 `$ w - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));1 e! D# v+ ^, t. M8 E; A6 s
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));7 q$ t; I. ~/ A( e6 n
- $members_preferential = M('members_preferential');9 j0 `) [! g$ e/ G, f0 n
- //对应投资金额,
+ m" j6 j+ r7 S3 p$ S - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));5 M x' h8 @+ C5 h( Q. V9 g. M
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
, a1 S# l: R$ G) S' U& ?* Z o/ ` - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间6 [/ n3 ?/ H" |# K9 R0 g1 Q
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券1 i3 T* a& C; g
- if($save && $add && $add2){% j5 q9 e [* |( G( e
- //事务提交
; f( ^, Z: a7 Z3 v - M()->execute('commit');
0 G6 Y& w" f7 ^6 x5 H+ E - $data['msg']='恭喜你领取了'.$num.'元优惠券';
$ O: F1 y6 [/ A8 L/ V* Q - }else{8 H; V) T! j/ u4 c
- //回滚
! z& r+ B& F- k2 ]- e# N( e - M()->execute('rollback');
) \1 S7 P8 J4 S - $data['msg']='未知错误!';) {; m' ^" K7 X7 j ?8 p6 B8 `4 E5 G9 _
- }8 G1 D( ^. b- @4 H3 ]1 {7 @
- }else{) J2 _% O$ _% i
- $data['msg']='红包已领完,你来晚了!';& a# I" p R9 r) d; |: }; S& L
- }
N. X$ a5 L6 B - M()->execute('UNLOCK TABLES');3 f5 F+ H+ Y1 T# [
- }
/ n6 P9 l$ w ^- z# a' ]% z - }
+ W8 x! }8 t ^/ q# R) i. _ - }else{
6 ]" x% E( H, m2 e; }7 ? - $data['msg']='非法操作!';$ o3 O0 @- Y2 k' i. n& {5 B2 V
- }
6 D" m9 ~8 Y I1 l. d# M - }else{. g8 ^" a4 C2 W
- $data['msg']='还没有到活动时间,请晚点再来哟!!';/ h" b9 z+ z2 K! y" x
- }0 k# h8 j) A; v/ S* E. j9 F
- }' U, R: c( F- ^& _; d4 N
- exit(json_encode($data));
1 s. d* F* Y: _% K" G j# s) C - }
复制代码 , X3 R& Q) y) H: N1 K: b
& R8 {3 d4 {6 S) e8 i
|