模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 , n' v, P3 n8 ]7 m: ~
Mysql中的锁语法:9 f2 X/ A* d* G6 E% ~7 H
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
4 B$ x* v. ?5 u9 Q+ bUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
7 U! S. A% |: X, U* p7 uWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
) Q9 C) \) V( }注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :( s1 p& y0 \/ u9 h
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。: c8 z4 B; ?- p/ ~. K% _
测试时,有个文件就行,叫什么名无所谓 总结:
, u" X/ X/ [0 C) N5 d/ ]项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
! O, c$ H e; ~7 U1. 高并发下单时,减库存量时要加锁
T+ w$ ~& }! J2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
/ C4 V# }) R% \, X* N: d - 模拟秒杀活动-- 商品100件. _! s6 @* O9 f6 W9 K
- CREATE TABLE ta
2 Q. Z6 j. F1 u, ]* X4 f8 l - (
2 F7 O% { Q. F2 K8 f% _1 S, G - id int comment '模拟100件活动商品的数量': f. A6 ^/ D4 R& m, Z6 S; a2 v+ ~
- );
' V% Y1 ?/ |# ?, w+ J0 B* [ - INSERT INTO ta VALUES(100);
' _6 P8 M2 |/ a% @ - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
8 s: N5 R+ @% i5 m$ K7 a0 Q - */ 3 I3 G. ]* H* M3 o9 G
- : O7 f% p8 v! C- H$ {
- // 关闭错误报告& F% v3 e/ ~4 }- W
- error_reporting(0); . `1 R% o( `- h2 \" d
- , N: v9 y# r5 F1 C1 Y
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
# E. h$ w4 W- X. t. _( Y- ` - $dbuser = 'root'; // mysql用户名
( B r) i2 o' K) I$ K! c+ B5 M, i4 L7 G - $dbpass = 'root'; // mysql用户名密码
D6 Q# `# Y! i+ R6 X - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);$ t5 E, m- ]/ p5 }; W6 H" {' O
- if(! $conn )% i/ o2 e" z9 m; v
- {2 K6 u3 }5 ?! s* L
- die('连接失败: ' . mysqli_error($conn));. y" m5 c+ B f) Q( G7 ]
- }" l9 r$ j' u5 S- m3 v
- // 设置编码,防止中文乱码( @3 G- ]- ~/ f( e) F1 ]) q9 f
- mysqli_query($conn , "set names utf8");
% B) \9 B) b" x; [" r; ^& u2 k% _ - mysqli_select_db( $conn, 'temp' );
" K& t0 G5 }$ _7 d/ H2 ~1 L - ( l& A& K' I5 j: n- z4 k
- # mysql 锁 0 ~$ K* U. J. G. { ~
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
& O/ h3 I* L8 Z) v1 o - $rs = mysqli_query($conn , 'SELECT id FROM a'); 6 {' a8 H$ r% y& Y8 b7 ]
- $id = mysqli_result($rs, 0, 0);
" U7 _# O/ }* F. c; \) m% J1 @( z - if($id > 0) p; Y! f+ A. Q
- { # U |! }4 N* I2 d' p% h) b
- --$id;
" h% ^9 Y( P8 W6 I1 y - mysqli_query($conn , 'UPDATE a SET id='.$id); 7 u: r/ R3 D/ {- s, @4 M4 f
- }
( l' d$ [8 p& K - " J; w2 \- j7 `7 x6 V( a1 F, k
- # mysql 解锁
+ z8 S4 k6 A! k5 u! D, T. k - mysqli_query($conn , 'UNLOCK TABLES');
: P( U( H# T* Q# i! o: N - //查询解锁后的id值5 i5 q+ p- q8 `7 _" [, G9 b/ y
- // $res = mysqli_query($conn , 'SELECT id FROM ta');9 r- a5 \# Q4 r2 k1 q" k
- // while($row = mysqli_fetch_assoc($res))! b: m* [ ]( v
- // {& c. w+ B3 V0 l3 K. ]
- // $id = $row['id'];8 ?& D( c4 o' o) c1 `5 g
- // }
$ |2 ]. ^1 Y1 Z g - // echo $id;
复制代码
8 ?; V/ d2 ]2 `, d. u6 w' @& q! e# ]1 R7 k, ^
: s( n- N' `4 G" z
PHP文件锁示例: - /*
4 O$ h( s4 N7 P" A- b/ T" ~ - 模拟秒杀活动-- 商品100件( F& S) Q; ^( O# M& N) W
- CREATE TABLE ta5 m' i ?3 H0 r2 A6 G
- (
$ ~& k# J1 {! C9 [5 Q% S* N - id int comment '模拟100件活动商品的数量'9 S/ |# F; o- G+ k
- );
# d; G# x- b: K! U0 c - INSERT INTO ta VALUES(100);$ p' V1 r/ a# e* e1 r; h
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
* Y! f) }2 Z7 y) F3 B, R1 s. ` - */
/ T; K5 e! [7 c2 E2 m( Y -
7 f1 {) o! `$ c - // 关闭错误报告. c4 k; i1 C3 z5 I, M/ a8 d
- error_reporting(0); , b& h8 p- \0 Y) E' r) S
- ! C$ I- n9 z E1 e' L" L3 b& r
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
( A) G H+ M5 B5 `+ ] - $dbuser = 'root'; // mysql用户名
- }* \9 y ~. }) c4 ~) e - $dbpass = 'root'; // mysql用户名密码# D7 Q! H1 C, I& e
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass); \/ P# ~% E7 N' T' s
- if(! $conn )! T) _9 U, E- l; z4 R1 n6 m
- {
! T6 P" k6 [+ K, J - die('连接失败: ' . mysqli_error($conn));
+ e% U/ Z H% h/ n - }
; l- h" a. b. A9 ? - // 设置编码,防止中文乱码6 J+ U- |' Y; A) q: A
- mysqli_query($conn , "set names utf8");
! Q3 q' M+ u# r8 j5 {' V$ | - mysqli_select_db( $conn, 'temp' ); , P! i, s" {/ w. }' e8 M
5 a w* N( f4 n& l2 q+ f- # php中的文件锁 2 p5 H; {* [& c4 T" K$ j
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 b+ V. ?$ v: U3 w# O2 n' k6 ^: J
- flock($fp, LOCK_EX);// 排他锁
2 D* s1 C; T- e# S
2 Z( R0 B8 a+ k. I- $retval = mysqli_query($conn ,'SELECT id FROM ta'); ; A! b- d6 b! a. X9 ^9 g" e% ~0 R
- while($row = mysqli_fetch_assoc($retval))
1 T9 |4 _9 ]4 |0 k - {, }9 u( c$ k5 y/ D6 B* k7 ~4 i+ m4 X
- $id = $row['id'];. a3 |3 O$ }1 |' m- W5 K' L* u5 l
- }
$ E1 S. O* ?" \- A) U -
7 a4 _: }4 H6 t - if($id > 0)
/ n. R4 l0 D. E% f - { 4 _& i5 W7 O" O( m" E
- --$id; ' o2 O; W, v7 E: J; h
- mysqli_query($conn ,'UPDATE ta SET id='.$id); " S1 s- z& ~3 K0 T& B) ^
- } 4 }. u# j: m& @; t
- # php的文件锁,释放锁
& [7 I8 g8 ]5 \ F! i - flock($fp, LOCK_UN);
6 h& r4 Y8 M3 s5 [! i' [ I - fclose($fp);7 O! S, `2 Y3 M* d7 }# `
4 l) h" }! v% ]# i& y- // $res = mysqli_query($conn , 'SELECT id FROM ta');2 _$ w6 D X9 |8 B! f) Z4 X
- // while($row = mysqli_fetch_assoc($res))/ u5 ~3 v: q! K0 p- d
- // {* q+ W7 I& W0 v* |3 a
- // $id = $row['id'];6 t9 n6 G+ `$ Y6 t6 } e% [
- // }
% F' M# P: ^& [! V9 n - // echo $id;
复制代码 ' A/ t$ S$ j0 N0 Y+ ~
7 x% q: k, l1 L2 v' _2 r7 K0 m7 O
抢券活动实例: - public function envelopeSnatching(){
2 C8 D5 V3 H& G - $lingqu = $_POST['type'];
7 z6 h' q, C! p9 q) N - $uid=session('u_id');//用户id$ [1 @8 p; i& {# A
- if(!$uid){
) X/ n; D3 P/ ]6 e - $data['msg']='您没登录,请先登录!';2 _2 X, v/ @) ]% M0 K0 ~
- }else if(date('Y-m-d') != '2017-12-12'){. M$ `$ ]+ g9 M- [- K8 R. ~
- $data['msg']='不在活动时间内!';4 @( c5 C& a$ W( r# r4 c2 J
- }else{; S/ C/ q! R. y% j
- $hours=date('H');//当前小时数
- |5 I8 o: L: s; ]% p# y - if($hours > '09' || $hours > '17'){
8 t) Y, i( W* O' }/ P - " [; N' I% t6 i5 \; B* ^2 c
- if($lingqu == 1 || $lingqu ==2){//点击10点的
0 I" V4 z) i9 U7 O$ T4 `, ~8 v - if($lingqu == 1){& [& a1 A% I4 b# n
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过/ W: N, e; I5 m
- if($hours > '09'){
, ?0 u" ]9 r2 B - $num=mt_rand(25,28);//优惠券金额$ {5 k0 K0 Q% X& x. z
- $id=1;
+ \3 t/ T* n9 c! f; z6 P1 l9 Q - }3 S, A/ ]) u6 t* b/ i' ?
- }else if($lingqu == 2){& @. a+ `; D8 {" w/ M8 Q7 u
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();! y$ L& j& S9 f9 l7 T1 z7 B1 I
- if($hours > '17'){
0 R9 R: a Q' t6 d" f$ ~ - $num=mt_rand(50,55);//优惠券金额
% y5 Z. B- p6 @% e1 g$ p; c- L' P - $id=2;' s* a$ j/ r. L0 F4 w
- }
( {/ J, N6 [* M* Y& c) P- v - }" |3 ^4 u, j- p! ^( N
- if(!$id){
- h/ F! ^ v7 t7 y4 H- o, W! l0 Q- q - $data['msg']='时间还没到,晚点再来吧。';
. _( e- H0 ]; r" | - }else{6 s, N0 g1 A s5 g0 ~ K5 `4 R
- if($is_lingqu){
* Y, ^& h3 W! g( F - $data['msg']='你已经领取过了,留点给别人吧!';
. S2 V& L" z( Y9 y - }else{7 G# T( y7 t, [9 t' h& |4 D2 F! \
- //锁表! l! ?+ O3 ?: r0 R
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
- H1 }/ ?( s4 w! Z: {7 d: s; N* b - $active=M('active_num')->where(array('id'=>$id))->find();
g4 R+ i! e1 M- x - if($active > 0){0 X) `% F5 i2 F# ^0 D
- //开启事务/ \7 W O1 f$ @0 e4 G: f9 r
- M()->execute('start transaction');% ?1 ?- K) L0 r+ f4 m
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
1 o5 H' o# {+ `' s0 Q2 I2 ?4 m - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
. y* t- ^/ d0 `+ E. J" C - $members_preferential = M('members_preferential');
( J2 G9 H( t: B9 p& J- F) f - //对应投资金额,2 Z1 q8 p; U0 l1 _ [
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));, f- O9 y' b/ O. O6 N
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');( D$ }$ g7 ~6 K. a( {3 i# C* e4 t2 L
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间: ] Q! ^( i/ u2 ?
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券0 _9 ~7 J2 T0 t% C8 U
- if($save && $add && $add2){
: ~2 E* ?6 w! @: S - //事务提交1 q h4 \& Q( a) |' Y+ y+ t
- M()->execute('commit');
* q, l [# P. c2 `) r( r: `: k - $data['msg']='恭喜你领取了'.$num.'元优惠券';3 m3 G y" n5 ]0 _
- }else{
5 c; b2 h! I. e- Q- G& e9 c - //回滚
7 K: O5 k) _. @. ]0 I' K# ], S - M()->execute('rollback');0 m& W( R2 Q4 f! _
- $data['msg']='未知错误!';/ {: T/ o- A" T) z' N0 @ E
- }
# z' `4 e" F! t* z' b( _ - }else{
% d. J8 I$ V0 |2 s h - $data['msg']='红包已领完,你来晚了!';
5 V9 M7 Q H/ z& k a - }5 e" a5 c/ G/ y( B: c
- M()->execute('UNLOCK TABLES');
( \- w& K: q" h - }
! ^ i7 \6 ^: T3 F# G - }
% Q2 ], K7 H9 h. e; r3 h - }else{
5 F5 [4 b8 N( \/ B" p, s - $data['msg']='非法操作!';
/ H3 q9 X- z9 _0 y7 U# } - }1 ]; _6 t' D7 T. H+ T' C2 r: i z
- }else{7 ^" R4 p. j: q4 N- X! i. g. Z- G
- $data['msg']='还没有到活动时间,请晚点再来哟!!';1 z, V( Q# F( h% v8 v; \
- }
- ^9 g2 L/ s3 U {% T3 k$ S - }. R! _* F! h0 X% C( D
- exit(json_encode($data));
# e0 ?0 o8 Q/ V8 e5 H& v - }
复制代码 - X5 {3 p& U; l3 B; {
. _4 a6 x8 P4 u3 M |