模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 6 _. {5 D" N! u. E( F
Mysql中的锁语法:
; S$ E9 K' D/ B; q$ @9 T1 vLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
r" i: b0 f9 TUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表2 Y3 E Z. K. C4 I, U' }# W
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞: r9 B8 s" M- X5 ~3 _. x" O/ ]
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :% w" }; v. i h
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。$ g. r2 V- F" N ` D. h; k1 h) G+ U
测试时,有个文件就行,叫什么名无所谓 总结:
/ g; ^! Z, R% j# L4 B; O% K项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
% m5 l& \3 e. ~/ P; W! v* }( `1. 高并发下单时,减库存量时要加锁
; a+ [- e- B3 Y. `7 n4 M! k; k2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
$ F6 v0 p1 z* R' P% [0 s - 模拟秒杀活动-- 商品100件& b% H8 H- `7 K3 P# {9 c/ x) @, `: `
- CREATE TABLE ta0 W+ U9 r. N4 @6 f
- (# B& r' ^7 n/ C: ?. w; |2 l. l# |* q
- id int comment '模拟100件活动商品的数量' n6 J9 u# {1 I0 [9 a2 m. I
- );
; M- R' s! o; d - INSERT INTO ta VALUES(100);
- x: Z. V$ Z$ j: r- K) { - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件1 L" t+ U$ K' G! S
- */
% _, n! ^ I' k - & f2 f3 C* Z( h) g( y1 [
- // 关闭错误报告" t/ m. W5 H. n. k/ K4 \3 K
- error_reporting(0); ' y- K! c: x$ B P( x0 @
- 7 t! O# u6 [( Q: }/ y6 L4 E) N
- $dbhost = 'localhost:3306'; // mysql服务器主机地址) Q" o k& h' Z1 k$ M4 }8 W
- $dbuser = 'root'; // mysql用户名) b7 u v! x1 c h- _5 }+ \: \; D
- $dbpass = 'root'; // mysql用户名密码
% b* `5 x) M+ _- V: E9 O - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
, \% Y7 [' |% P& _; k7 H/ E - if(! $conn )
& e; D# ?# N# D/ D: ? d( d - {) d/ v% k: h9 e% ? U9 H* T5 A
- die('连接失败: ' . mysqli_error($conn));* q2 D l* J4 I) ?0 i
- }
7 M) b) B+ y$ B2 o - // 设置编码,防止中文乱码
0 z% J2 n3 S0 n0 F4 P+ \" H - mysqli_query($conn , "set names utf8");
; c* N' K0 o1 a% b - mysqli_select_db( $conn, 'temp' ); $ ]; ^1 d. e9 E, n
- " a' T$ T: u% j/ @
- # mysql 锁
/ @, P ?7 @# x$ w1 W4 B7 j - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
+ Q" h( t. u2 o- n - $rs = mysqli_query($conn , 'SELECT id FROM a');
" r. @ t0 R( D- K8 b0 I - $id = mysqli_result($rs, 0, 0);
, s7 n2 O1 B7 \ - if($id > 0) 6 g" t& T, x( p, u8 k% B d3 b5 Z
- { " Q2 s" }6 X* M/ ?' o: r
- --$id;
# ?7 U3 i# i- A" ?, m/ @0 i% u! u - mysqli_query($conn , 'UPDATE a SET id='.$id); / K9 H) v. }, P) M
- }
; g- S* g* b! k+ J+ m1 b1 R - % M+ K/ w, o9 z* i' V* |- e) g
- # mysql 解锁 $ q1 ~- l N, U6 b
- mysqli_query($conn , 'UNLOCK TABLES');
8 k3 ]2 l( A) W. R6 G/ u" f - //查询解锁后的id值; T& s& ^6 h: M) |1 `! g j
- // $res = mysqli_query($conn , 'SELECT id FROM ta');! L- ] H# B" K( ?5 y' P; k* e
- // while($row = mysqli_fetch_assoc($res))
9 X/ t, Z. ?" p, i+ T" R2 y; u - // {
' J0 L( b3 x/ P) a% s0 @ - // $id = $row['id'];
4 k6 h) }+ s$ m( Q0 J, P - // }" r4 m, l( G% G. P8 _
- // echo $id;
复制代码
: F) ^% j& B; i& S2 T. o- A) {: f5 ]% M9 Z- k6 l
0 ^' N& w9 S# I- c5 {, nPHP文件锁示例: - /*
5 M; M$ b* K! T3 U) d3 A: u - 模拟秒杀活动-- 商品100件1 ~$ M) G6 C' W- G
- CREATE TABLE ta
9 K& W& D; z t* r, r - (
9 [: O. r% \! f' [% o+ I2 E - id int comment '模拟100件活动商品的数量' C: e( w) }; V. n" b6 S1 Q
- );
! F* b+ V& S3 b - INSERT INTO ta VALUES(100);
! e& ~8 p8 }$ I - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件# D6 Q. C, H9 R
- */
+ K0 @8 Q+ p! W' { S -
- r; f$ v- w/ H' x$ X. `' T5 M# T - // 关闭错误报告
& C2 o) @6 b9 Z6 m4 b* z - error_reporting(0); ' A* N) z& @- U u+ [5 M
- , L- C+ F: D, w* I: {; Y! Y
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
4 c6 G6 {5 \9 x# _ - $dbuser = 'root'; // mysql用户名
: u9 H0 ]2 ?" H3 p; a - $dbpass = 'root'; // mysql用户名密码8 P1 }, i- M& n0 g: M( f+ G" k
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);' @# U) \3 V2 L v% W, ]/ {
- if(! $conn )& B# w6 }% }$ B7 B' f$ Y
- {' k- A# a+ u$ @$ [% s3 ]5 K4 p
- die('连接失败: ' . mysqli_error($conn));/ n: g: o( [6 t& s
- }
. J2 ^: U: X* B) p& {6 W# | - // 设置编码,防止中文乱码3 W$ ^" k- r# y+ ?: r7 e& B5 K$ K. M
- mysqli_query($conn , "set names utf8");
P c6 C0 S+ e" ^* C8 s& t4 p0 b - mysqli_select_db( $conn, 'temp' ); + S- o. A3 r& K* H: `' Z8 Y" c
- 6 R# o) B$ w" |( q. s) d% s/ q C
- # php中的文件锁
3 n& g/ Y, p$ ^* n# y5 L - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
2 E( r% o- X& M" [, r/ y - flock($fp, LOCK_EX);// 排他锁
3 M9 \, \" p7 P2 K/ W
; Q* }) d( h& g1 y- $retval = mysqli_query($conn ,'SELECT id FROM ta');
1 c e$ F4 W- i5 y1 F. z - while($row = mysqli_fetch_assoc($retval))$ [( a H2 K* @1 K1 b3 X2 m3 R
- {
; _$ c/ L/ R, d% D, k+ d) d - $id = $row['id'];
/ A* i: @4 P3 y - }+ O: l9 |1 q" U" r
-
6 x$ q- @; s+ J% m7 O* a - if($id > 0) ; h; G* U& Q0 Q* c, ~
- {
+ L, h1 V Y9 o% _( `8 U4 F& q5 d; G# h - --$id; ) D" D' i. V |6 w5 l, f* A2 i6 B) T4 m
- mysqli_query($conn ,'UPDATE ta SET id='.$id); + [# ^$ C; T( p' E
- } 3 i3 x0 ?+ o) S7 d, Q1 u
- # php的文件锁,释放锁
% D: a. ~3 Q" z6 u+ B, p - flock($fp, LOCK_UN);
7 V% r+ o( M% K1 M/ { T7 ?4 s; \, c - fclose($fp);5 N' W+ x& x$ S0 r( n
- - P4 C+ J5 H! T
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
( d: W/ s+ ^* ?9 ?% H - // while($row = mysqli_fetch_assoc($res))& o9 f, J$ u+ J
- // {
+ s" A$ R8 i, G4 Q$ P; v - // $id = $row['id'];
7 H5 M/ S- t5 K+ D/ c$ l$ T8 }. E& _ - // }
# l* ]+ h% W) M0 I( M - // echo $id;
复制代码
9 p, S5 I( @, D9 m% w! \# ]# s) m- k4 Q; l$ f
抢券活动实例: - public function envelopeSnatching(){
8 h- M# G# X- o! x - $lingqu = $_POST['type'];
% z0 y+ g1 Q/ h: R, v/ N( X - $uid=session('u_id');//用户id
% C- L, e8 _& v5 r. J& O5 H - if(!$uid){
& r2 N+ l8 n, t1 O" [: _4 H5 k - $data['msg']='您没登录,请先登录!'; b( \ X) ] i Y* ]8 J7 p
- }else if(date('Y-m-d') != '2017-12-12'){
! P6 ~7 Z% |! A8 G9 j - $data['msg']='不在活动时间内!';
6 @1 t& {9 M& A# E \, G - }else{. Q- \: D; I/ G' ]5 E
- $hours=date('H');//当前小时数! r) {' _! q7 U- o
- if($hours > '09' || $hours > '17'){' D4 O* a7 t8 Q, y, X
- 3 C. W* r3 ~0 n( ^
- if($lingqu == 1 || $lingqu ==2){//点击10点的: e ?1 g4 K4 d, Q: l
- if($lingqu == 1){) r* E% i9 a% o, O a. |3 V
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
* h' r/ @1 r: j8 T/ @' H6 M3 d - if($hours > '09'){6 {% Y& N3 { c8 }- e0 h
- $num=mt_rand(25,28);//优惠券金额
h b5 J3 l3 [; A3 Y6 z6 a - $id=1;
e# E& w8 b. [4 ?# H" Y - }
# |9 Q8 s1 @) I2 g. ?& j - }else if($lingqu == 2){
/ B, X, j1 L6 w, w8 b" P' Z3 l - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();: _9 i0 ~4 `6 L, T* _9 o- s
- if($hours > '17'){
2 ^/ F( v$ _4 o" L) u; ^ - $num=mt_rand(50,55);//优惠券金额
5 f0 f- ^( l4 D, G @- m1 ^. w - $id=2;+ U0 F1 W9 Q$ U' Z6 S! q3 `( b
- }' Z' M9 C l8 \8 `7 P$ u: B' @
- }1 T$ |" C3 f+ |- {& F
- if(!$id){* J2 v, u; J6 Z+ ~/ a; q9 t/ c
- $data['msg']='时间还没到,晚点再来吧。';
8 ?( c# U: W0 ~8 E3 ], D: e: |, Y8 d: N - }else{! l- P$ I+ j; h
- if($is_lingqu){
+ c/ F9 {) c7 r1 y; | - $data['msg']='你已经领取过了,留点给别人吧!';
% g: v: b5 ~* Q$ L - }else{1 ]& R$ p+ R3 P1 i5 L, h
- //锁表
7 m" e0 O: ^! f. W7 ]- c3 t - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
7 x: z$ Q# F$ K; I; L: ~; v - $active=M('active_num')->where(array('id'=>$id))->find();
o" r/ [0 j/ J C/ L - if($active > 0){) S3 x) b/ h6 l, U6 c" [& k0 H! C
- //开启事务
: M6 t+ v4 n& O& p# G8 f* f - M()->execute('start transaction');
* B; B- l/ _3 Z" c5 |& Z& u - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
! l- Q7 J J0 ^ - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
5 \! }5 H* A! Z# f4 N$ q - $members_preferential = M('members_preferential');
1 q$ Z3 U! e9 r- x - //对应投资金额,
4 Z+ O9 \9 \. y& e) Z8 I6 d- h7 ^ - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));$ t3 w6 j+ g6 i; W+ c* o8 L
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');- o6 r+ T* Q& J7 A: u8 @
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间: Y/ G0 M: `# B
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
0 ]: ]; s# @3 ~: Y' O - if($save && $add && $add2){
( _- {2 X9 l5 s* ^( J. w - //事务提交
8 s, Q4 b) d3 t% o" Z- ? - M()->execute('commit');$ P' P- f& _: y" `% A: k" s `
- $data['msg']='恭喜你领取了'.$num.'元优惠券';" E3 w3 B( x' Q V0 p$ q
- }else{
$ J4 l1 c Q0 W7 }' l - //回滚
3 V. J# u% k" W& M9 b - M()->execute('rollback');5 r' S. s7 C7 n
- $data['msg']='未知错误!';# ~. `: `# y: P* Z3 g$ C0 Z0 P
- }
! l( Q4 t8 q0 G/ H* o s - }else{
: W5 Y7 d. s0 @# m - $data['msg']='红包已领完,你来晚了!';4 L; M) h2 c9 W4 F: e# Y: r
- }
; z t; c5 i" a8 j; B - M()->execute('UNLOCK TABLES');% G4 I( P. f, x5 [- B
- }( ]6 f( W$ H0 e
- }
0 L( w( i& r+ J - }else{# H9 L- B1 h( J3 Q- H
- $data['msg']='非法操作!';
* `6 n* K+ r! Z" k - }
0 }( D* z! @* p" A - }else{ b2 v2 o5 M9 `
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
) y; e* u0 {3 S+ g+ E- c - }2 }# T" o; i% g
- }7 z8 S: Y1 @( {& t+ c) ~0 O
- exit(json_encode($data));5 F1 ]3 `. _* v4 g2 K3 o; t
- }
复制代码 # k) M- l* _, s3 Y3 M
4 T! r' x+ w# M7 p |