模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 2 ^0 \- S2 d% A/ J; `( G
Mysql中的锁语法:
; K' w, V# d8 w8 D: z3 vLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】) _( b$ v/ p( m# D% O% h% V
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
6 Q+ j, [, f% J9 s0 MWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞8 U3 q# C2 i0 V
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
1 a& r. l5 b( f* k: T" h/ B文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。/ f' Y& [" c/ `# {3 T) k9 M
测试时,有个文件就行,叫什么名无所谓 总结:& r- z S8 g( Y. C; Q" ^' i# Y
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:+ Q: a. L* U! _. R6 Z4 m; j
1. 高并发下单时,减库存量时要加锁
" {- I/ _$ X( A+ g% M* J! D) {2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
- R8 W* r2 c3 a* w* q; M - 模拟秒杀活动-- 商品100件
3 g; l7 S7 y' y# \1 A: z5 c" T - CREATE TABLE ta
& A& u# `; l2 b6 A9 A9 B5 Y! t5 t; P - (
' c3 n5 d, j( u) c - id int comment '模拟100件活动商品的数量'3 `6 b5 b0 Q) T9 z, S5 U3 F
- ); R. ^- @. X, t! ?7 {
- INSERT INTO ta VALUES(100);7 p' T, Q4 ?1 o) _6 \7 G' D' ?
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件9 T1 o! [; s. Y
- */ 2 W/ Q( k) B# C4 _
- ; X( n# c9 C- n+ {: `, H0 d
- // 关闭错误报告7 ~! F" q4 q! o; R& K+ D m
- error_reporting(0); $ g2 D1 s% B7 n
+ u! h( x/ l' v r6 O! [* \& q( ?- $dbhost = 'localhost:3306'; // mysql服务器主机地址( d- {7 {9 v% F3 a. X1 c( ]0 s
- $dbuser = 'root'; // mysql用户名
+ d& o$ w9 K9 [2 N" F' u - $dbpass = 'root'; // mysql用户名密码
0 w7 u) Q9 A" l8 P& A7 i6 G7 @' P4 Q - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);1 e# t+ Z6 ]+ e4 x. s
- if(! $conn )
; R. K$ T2 w9 Y) A4 |, [ - {0 f0 O/ ~. ~. F. t4 S0 H
- die('连接失败: ' . mysqli_error($conn));0 W7 o# [' \6 o& r& F& N
- }
5 r; }3 q6 x% ~6 W' m/ k) I8 [ - // 设置编码,防止中文乱码
; Q' u! q3 [! u# o - mysqli_query($conn , "set names utf8");/ @5 N) {2 M. x3 |0 A
- mysqli_select_db( $conn, 'temp' );
; X: l2 \: B% e& u4 P! v( y. F; M
3 _7 X. J8 F/ Z5 f' B: q- # mysql 锁 " a% [/ f2 D& w/ [+ }
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 8 X2 c0 e6 i: Z% R9 B" j2 z2 _
- $rs = mysqli_query($conn , 'SELECT id FROM a'); % K# Y, X7 G: s& G% Z% N3 D
- $id = mysqli_result($rs, 0, 0);
$ G' u B4 X( R5 s - if($id > 0)
$ z% A' E- x+ u5 X; a# u - {
3 H4 i; o0 C( r" ? - --$id; - |. x. j3 L# q( i% R' P( K$ P
- mysqli_query($conn , 'UPDATE a SET id='.$id);
5 T- z7 g* I- I9 U* u7 ? - }
2 i2 ?3 G9 S- i O
- L9 x% {7 }- g& D8 t# g8 u- # mysql 解锁 . R a( d L' U1 H+ v& f
- mysqli_query($conn , 'UNLOCK TABLES');
" g7 s$ z5 ]% v9 [/ v& C9 w - //查询解锁后的id值
/ n: Z! j' [6 m. |/ p - // $res = mysqli_query($conn , 'SELECT id FROM ta');
_" @: c, `' f- r# w - // while($row = mysqli_fetch_assoc($res))9 e9 M9 k! Y! z+ \& i* W1 m( A: A
- // {2 D7 L6 F$ [8 G) D0 f* r
- // $id = $row['id'];8 z! K5 c3 _0 j6 J2 a0 n/ [/ @0 ?
- // }
& @2 h( Z7 j" i) ~ - // echo $id;
复制代码
# L( X4 O4 q) H9 B! P5 Y8 Y- n4 `# W, ~0 x/ g" W& O- r& G. f/ C) ]1 ^# D
5 y3 y" J$ m* A% K8 m, iPHP文件锁示例: - /*
( X& z9 b- P1 u1 B) V - 模拟秒杀活动-- 商品100件7 J7 U$ X- L' N& ~$ u$ w
- CREATE TABLE ta K' M. n9 p# r4 ^- E! w
- (
) @% a6 f- E: f# K6 d' G7 D - id int comment '模拟100件活动商品的数量'
& |2 {$ p) H+ r* R7 Q) m" x - );
x2 I- h) f% e - INSERT INTO ta VALUES(100);
& }9 @: d! {$ |" A - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
* q( T# x r+ @1 t* e - */
# R* a5 q) \ k! a3 [0 a - ! x: m& D) m4 W7 ^1 o9 f
- // 关闭错误报告, p/ K( D2 p P4 t- e9 z
- error_reporting(0); # S; x1 i @2 M" u
8 n, m; {! x' N( ~8 L/ `0 @- K9 W8 s- $dbhost = 'localhost:3306'; // mysql服务器主机地址
; @) T& Q( `* H) s' Z - $dbuser = 'root'; // mysql用户名
% S* b4 D$ e/ A* g; D - $dbpass = 'root'; // mysql用户名密码
( q) y8 `5 w& C' I8 p8 ] - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
. P# R. y6 x% t' _ - if(! $conn )
p, y( ]6 w- c. l - {0 \3 @% v" s2 X! n! u" l
- die('连接失败: ' . mysqli_error($conn));
) W2 p$ W* e s# K - }5 u: N5 W e- \# ^7 ~ O% K9 ~% ~
- // 设置编码,防止中文乱码' V& f, h, l/ ?6 b: X
- mysqli_query($conn , "set names utf8");/ a7 S4 j0 m5 D7 o$ i2 B
- mysqli_select_db( $conn, 'temp' );
' H5 N1 T: x; o9 p - B* p: ?9 g4 [1 G/ a
- # php中的文件锁 8 G* m( a d" v9 |) ~
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 5 n: U5 M$ N1 Y
- flock($fp, LOCK_EX);// 排他锁
: \0 H, ^+ m' A, k& I5 Y - ; x2 d; a' H; \/ s$ t( s6 W
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
1 e1 F- Q& A; _6 r0 K - while($row = mysqli_fetch_assoc($retval))9 B- A: b% E! M1 R3 l/ j
- {
: Y- _ Q! g5 \; Q S* n" q - $id = $row['id'];7 K8 U4 \0 O" y8 e
- }7 s! ~+ V1 o, W, M8 g
-
) t& x5 N$ u- G% Y$ Q9 M1 ` - if($id > 0)
9 k1 M5 [ S) `* }( d - { ; w( u; L. k3 N# q) q
- --$id;
, v. p4 Z8 f6 _* C4 ~ - mysqli_query($conn ,'UPDATE ta SET id='.$id);
1 h" K4 M3 r/ ~$ K1 l - } $ X- w/ m! {3 b3 h! g$ a5 I
- # php的文件锁,释放锁 y4 a9 q$ k' g1 U. H/ d
- flock($fp, LOCK_UN);
8 `" f8 I! v. {. v) v7 k - fclose($fp);
: b$ h) y! M3 F6 ^& E& t - # m! M7 A$ `5 {# d
- // $res = mysqli_query($conn , 'SELECT id FROM ta');- r& h, k0 j/ s* _5 Z
- // while($row = mysqli_fetch_assoc($res)). d( ?6 ]2 Z) V) M# w& S
- // {
1 F" a3 U; [" H% x: o2 T - // $id = $row['id'];4 {8 Q1 O S) z: C! U: g
- // }
! @$ U& t' }4 e( K$ N" D - // echo $id;
复制代码
4 v. E0 l2 ?- K
1 t- t5 @# ~2 s抢券活动实例: - public function envelopeSnatching(){
) O+ ?# n. h" G( s - $lingqu = $_POST['type'];; V3 W9 m0 B M6 O" T- X
- $uid=session('u_id');//用户id
/ }1 P" e+ t( d4 Z - if(!$uid){( M6 E1 }6 K5 u* i; R) Q# j& z
- $data['msg']='您没登录,请先登录!';
1 B' G. F9 s8 K/ ~" F( ?+ \3 A& j( T - }else if(date('Y-m-d') != '2017-12-12'){
- D( b5 m' E* B" |! M. n3 H# j - $data['msg']='不在活动时间内!';
" ^$ H9 S$ d0 r u - }else{
, |3 m6 ~, h0 ^ j+ X% f0 E - $hours=date('H');//当前小时数
$ O' _6 z- Z% ^3 g! ^+ V$ P6 t - if($hours > '09' || $hours > '17'){
. F# L. n. J6 P& M: e l0 L" | - 5 S7 ]: d# h. A, e
- if($lingqu == 1 || $lingqu ==2){//点击10点的. [, |, K7 m1 \5 u& F
- if($lingqu == 1){+ F' K7 E8 }; C% Q( w! e, s/ Q W! L
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过 a$ }$ ]# B" [6 d. E) y2 R% x o
- if($hours > '09'){
0 d2 m" ^: i: l/ C2 ]% P& T+ U, O - $num=mt_rand(25,28);//优惠券金额
7 a4 H7 ?7 M& c" @, f: f2 N% Q - $id=1;% I& ^2 K3 l; G/ L' i7 u
- }
* R/ q p q* J0 O; \ - }else if($lingqu == 2){
( [+ m5 S' R; T1 @' S - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();5 C8 D! J- {/ J" R* I
- if($hours > '17'){
% Q E" a. O$ a) q5 s - $num=mt_rand(50,55);//优惠券金额5 `- H: k4 H5 T# A# }/ B1 M
- $id=2;" {6 |0 u0 B) I" ?
- }
' s; ~2 x" l- ]6 x0 U9 U. r% a9 Y - }
" P) e9 V: p# ^ - if(!$id){) L3 S& n2 i, R# ]! `, ?; G
- $data['msg']='时间还没到,晚点再来吧。';
+ {& @7 Z" s5 M% ^; d: U - }else{
# f! E6 ~9 d! o. W( i - if($is_lingqu){+ U% C6 N5 U# Z6 M
- $data['msg']='你已经领取过了,留点给别人吧!';
; Q: v5 X& l2 I$ w& z - }else{
$ x, @& A- [& w* B; @ - //锁表$ p p5 q' s9 e. Y4 u; y# H
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');5 x8 c" N! G2 f# J
- $active=M('active_num')->where(array('id'=>$id))->find();
: F* g* O* ?) I3 D- v - if($active > 0){# j; M+ S" g5 q7 j
- //开启事务, N2 `/ L3 z+ O: _8 A& E- E; O# B
- M()->execute('start transaction');( c; U2 O0 E& y! R
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
- c- ^- u2 D9 o1 a @0 F7 n - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));) [) `( t$ \& Y0 j* m3 |
- $members_preferential = M('members_preferential');' S; V; q3 m* w* |
- //对应投资金额,
/ H: }% t0 A/ V; I' ?6 b6 y - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));; N; a8 O6 m; z; G% c0 H
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
0 X' p1 |3 @" k - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间6 t# }/ @- P4 B, X
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券, b% p) ]+ N# S; |
- if($save && $add && $add2){ o5 T* ~! I5 N. k
- //事务提交$ r+ l" T3 t# @3 J
- M()->execute('commit');6 u. \5 `3 U1 w( n. \
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
% G1 ~' `- W% C( L5 _1 H - }else{; W( S: h. w) K, A4 u
- //回滚8 [8 f- E* [$ @7 d8 s1 A1 m
- M()->execute('rollback');3 K% J- G0 Y( t, J( X6 v
- $data['msg']='未知错误!';
& D* U* |/ T* b! X8 w0 ` - }
! r& i/ h1 Z7 `: D5 v/ q! w - }else{: ?5 v! {" S; C, [8 R$ p8 G1 q
- $data['msg']='红包已领完,你来晚了!';. j* N/ A) }& d
- }# ~8 C5 P$ B6 q$ T( @. e
- M()->execute('UNLOCK TABLES');, v. r- g* d9 Q& H# `$ q
- }1 H5 d+ m" ^; c$ ]1 x
- }- W2 ]1 ?( M8 M
- }else{
; U! A/ y2 L) P+ ^4 S - $data['msg']='非法操作!';
% I$ ~7 g8 b' u - }+ I+ P3 J8 v0 Z. M: a$ d8 l
- }else{
( h! f$ e5 ~' W- n - $data['msg']='还没有到活动时间,请晚点再来哟!!';
0 Q6 M' d2 @' B5 r h0 ~7 v6 }2 I6 z+ J - }, h3 z! |( ^! `) w
- }* ?& O4 U7 @; s, X1 p: N
- exit(json_encode($data));- u0 \; u/ f1 {9 ]" a8 r
- }
复制代码
2 G& Z4 ^9 O+ G+ F# S. A; y
+ R- t3 X8 E' S6 ?2 M& x1 s |