|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 1 f4 b# u' g5 u7 u' t5 Z( d0 e6 E
Mysql中的锁语法:/ j0 T: M% _1 W2 j% {( i
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
* `' H) u; z# J0 ]UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表, \- z2 j$ F, g. X: ~
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞) T9 w9 _, M6 m) }
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
# ?! c9 z9 g2 x0 w) o, b! ~" N文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
r! R# t, B! M6 k" G# u测试时,有个文件就行,叫什么名无所谓 总结:
# u# p/ k/ `# S2 D8 d# N7 v) q+ a项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
2 M$ \5 L, V& F# z$ |1. 高并发下单时,减库存量时要加锁3 Y, M, a0 X0 N; r# j u
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
& i+ N) w3 n6 V7 a: ?% M/ F - 模拟秒杀活动-- 商品100件
+ D, I+ L. p' O& W# R - CREATE TABLE ta
0 a* N- J9 `# I. C. H% J& Z - (
9 ?6 w- L5 W5 D3 M - id int comment '模拟100件活动商品的数量'* H6 b+ S9 F& |
- );5 i; k& |" A# M* y% E# r3 _
- INSERT INTO ta VALUES(100);: [2 m& N# [$ T" F
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
$ A: q; `( n3 x4 b0 w6 H - */ " ~! _4 S( R+ @$ ~
-
2 ^% n0 d3 R# {* h: y" g7 l- i - // 关闭错误报告
& L( I7 X1 C1 _8 s. V - error_reporting(0); 3 o2 r a* s$ s3 I4 P" Y
% W. @3 b! F; ]$ j! x) S$ I- $dbhost = 'localhost:3306'; // mysql服务器主机地址
% v% K I! Z1 M, t( h' T3 i, J - $dbuser = 'root'; // mysql用户名
0 R- j0 r h! V1 d3 Z2 X* f - $dbpass = 'root'; // mysql用户名密码2 ~ \# W$ Y9 X- {: w& O
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
$ Z9 Z3 m, H6 F7 m! }3 E! T - if(! $conn )5 b" T: S2 Q; S
- {
5 J9 z' \% `9 ]* q - die('连接失败: ' . mysqli_error($conn));6 `) m N3 p# S9 B6 o* Y2 b( U
- }' _) c* y& }4 @! Q# k! Q9 q
- // 设置编码,防止中文乱码
7 B: y; m6 I* ?* q, o' M3 A - mysqli_query($conn , "set names utf8");6 S8 z4 z; V: N/ A) j2 i
- mysqli_select_db( $conn, 'temp' ); 7 p( o: |1 j T' G. s4 e4 u
- " y2 q' G' N4 f
- # mysql 锁 6 ?3 p) B& X' v0 e! S- g+ h
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 & }8 Q, P* m: D L6 P1 J w+ ?
- $rs = mysqli_query($conn , 'SELECT id FROM a');
3 a3 U; m( c' ~. C. f& R# P7 K - $id = mysqli_result($rs, 0, 0);
) {$ Y* g: D7 L! t* r8 c' w+ u - if($id > 0)
; U* y& K- P2 u3 ?( s; G - { ^6 @/ @9 I u& k) G; v. R
- --$id; 3 {1 `" i" U+ X" I% q2 n
- mysqli_query($conn , 'UPDATE a SET id='.$id); : v, j9 U. s! t6 F
- }
' a& ?1 K1 P; M0 z. R, q) [0 W
0 e' k) `3 @3 H! Q5 y% A; J- # mysql 解锁 ! O2 G$ r: \& A: }$ G' H
- mysqli_query($conn , 'UNLOCK TABLES');3 x& Q+ _7 L# g
- //查询解锁后的id值
/ _6 x, q8 a. L" R: T, @& | - // $res = mysqli_query($conn , 'SELECT id FROM ta');' H+ D% \- x* k- b( L; \
- // while($row = mysqli_fetch_assoc($res))$ a# f# K& r" c* Q9 F
- // {+ N' F2 [) e! s2 O
- // $id = $row['id'];
& N8 g6 ~" H; E( J4 x3 G - // }
" ?+ ]/ e) z$ Z( b, p6 N - // echo $id;
复制代码
* E" i. Z0 C, G& p; \: r5 Z8 A9 |9 ?! A4 g: j* I
# a/ G: u& u" H! W6 r8 ^" N
PHP文件锁示例: - /*
2 M* s) b. H/ l; [3 ?1 d/ O - 模拟秒杀活动-- 商品100件% N6 }1 i" ^/ }
- CREATE TABLE ta, c" l, Z% R1 w6 G! t8 Z7 w, l
- (0 ~2 z- d* B8 I) ~% W7 w: R/ O: L& a
- id int comment '模拟100件活动商品的数量'* S5 u- T d: N% I: [7 N
- );
' Q, R, b8 \; v0 ] - INSERT INTO ta VALUES(100);
$ t8 J: ]+ a1 i# E" W0 M& T - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件% L% \/ J9 e- |! h! J' k
- */
2 b, Z0 j# u. ^8 b+ a6 r+ D. W -
9 Q: c% i/ p: m1 K - // 关闭错误报告3 |0 D3 f9 f( e( ]( @. ]1 L
- error_reporting(0); 9 y ]+ Y3 G$ ~0 p$ `, L8 o
- * _1 ?$ R6 W; U. n1 l/ X ^
- $dbhost = 'localhost:3306'; // mysql服务器主机地址, d8 e3 G u- q! g/ ?8 R$ {
- $dbuser = 'root'; // mysql用户名
8 ^9 R, ~5 d3 H/ W - $dbpass = 'root'; // mysql用户名密码
- |# J8 @1 v, Y - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);+ |2 e( R6 V, Z7 t: G
- if(! $conn )! x- G C/ m6 \8 Y0 k
- {
8 ^* a* T I! b ^ - die('连接失败: ' . mysqli_error($conn));
- E& F* \ Q/ u. A' Z2 x) T+ k% N - }
0 M3 D6 [# n* b, j) E9 P/ ?6 j- P - // 设置编码,防止中文乱码
# m' ]% I/ E# h' V$ X - mysqli_query($conn , "set names utf8");
9 n, y' I$ D7 f, F* G- i - mysqli_select_db( $conn, 'temp' ); ( r- t2 l. Z2 M% I0 M7 l% m
D; P, x8 l1 x% u" y% N- # php中的文件锁
8 G1 `( R W/ Z - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 3 j* L2 |. z) M( v/ q" P) f
- flock($fp, LOCK_EX);// 排他锁 ' y4 b( P$ |4 S3 |
- ' B9 K D# `6 R! e/ o0 v1 c
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
* L; I4 U" @1 W. _ - while($row = mysqli_fetch_assoc($retval))
. S) B8 z- R" ~; z - {& ^5 L r L8 V" G: E% P1 N
- $id = $row['id'];
# \2 L: K- j3 i# ~ - }
6 M/ A0 e* y' b4 `! d -
! \0 U8 u9 f6 u; `! i$ N - if($id > 0)
$ u7 R1 V7 q* n" K - {
( K+ T" ]9 \# V/ o - --$id;
& {: V% t! i7 |! j8 W - mysqli_query($conn ,'UPDATE ta SET id='.$id);
' \' f" \$ \. b, f7 M - } . k" u" e! Z0 P
- # php的文件锁,释放锁 8 H. u: G5 k; N9 P1 K7 F
- flock($fp, LOCK_UN);
( O1 q3 } r3 m, v1 k0 k - fclose($fp);
) k' k) O! E/ P+ C
1 G) C" o3 |+ j0 c( ^% }* E- // $res = mysqli_query($conn , 'SELECT id FROM ta');5 ?( ^% D2 L' e( ?4 o$ K: m
- // while($row = mysqli_fetch_assoc($res))2 N0 J3 F. T& X0 h1 N5 F; R
- // {' }/ W L1 Q& K1 {9 [
- // $id = $row['id'];
7 D+ ~ }, Y3 e, a - // }
% V8 I* y1 J3 w/ t6 ]# w1 S - // echo $id;
复制代码
3 j/ f* G1 n& `( c; K% S. ]) X2 d+ o- i2 y6 r* X; q
抢券活动实例: - public function envelopeSnatching(){" q* D) R$ V3 I3 l! w6 M
- $lingqu = $_POST['type'];
* l. x* }/ q: l. g7 ~/ `6 `) _' Y - $uid=session('u_id');//用户id
! |% ]: f* O8 k - if(!$uid){. Y% i A- b9 F) p. T% E# c, d
- $data['msg']='您没登录,请先登录!';! J4 N& a" x4 y$ b) M4 ~
- }else if(date('Y-m-d') != '2017-12-12'){5 u" K. Q z7 ~. V! f- U
- $data['msg']='不在活动时间内!';
# @) @9 t8 `; X/ A9 L5 E0 ` - }else{
1 U" A' i4 c0 q - $hours=date('H');//当前小时数
( {0 C3 a6 z- ^0 x1 E" t - if($hours > '09' || $hours > '17'){
/ _: N0 E: N$ r1 l- r
$ C- [: n4 ^: `( i& R- if($lingqu == 1 || $lingqu ==2){//点击10点的
: Z9 L3 U5 |$ U, ` - if($lingqu == 1){
5 j5 z; R7 Q3 B; J, _ - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
& M8 l# P) A" v" \4 |" d - if($hours > '09'){
6 f& ]5 G1 U) D- g - $num=mt_rand(25,28);//优惠券金额5 f% y9 `; {& R7 d- n
- $id=1;
2 ~# M2 g( x# S& N - }
. D0 d) ?6 Q) i7 Z! ?3 \( m ^, O1 g - }else if($lingqu == 2){* T. j0 _. F2 \1 D6 L* s/ h$ I' |
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();- k8 {8 `7 I4 K O# ]
- if($hours > '17'){
" B4 i* o9 O- k - $num=mt_rand(50,55);//优惠券金额
+ G% S+ t5 ~" [9 M9 n8 @% M8 @' S - $id=2;7 u% o9 C# V+ f* Z+ g+ a. O2 z
- }/ E5 a* q3 c1 g+ r8 R$ V3 u
- }
+ q9 C. ?4 I% c0 o3 |0 @ - if(!$id){1 P! l4 W$ y0 ^6 B
- $data['msg']='时间还没到,晚点再来吧。';
- W4 w* ?3 _" {. H9 S - }else{! M; X5 v, u+ {2 [
- if($is_lingqu){
( i* u3 a% O1 ~4 z M* {9 d - $data['msg']='你已经领取过了,留点给别人吧!';$ Z$ C0 T* k3 w9 R" h
- }else{
/ @( R+ K7 s; N - //锁表
4 Z* d2 `7 Y3 J8 n - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');! L* q5 g* c' v* c+ F/ b* H0 i2 S
- $active=M('active_num')->where(array('id'=>$id))->find();0 S$ t9 q% G; r
- if($active > 0){
( Y6 |0 n- D9 x6 f - //开启事务
! [( O! a2 L6 o8 |: s! M - M()->execute('start transaction');
" d3 [1 X! z- f/ L7 Z - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
# ~- m {8 @/ ~$ M; R& z! V1 O; y - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));( a9 f5 _, y9 B6 { P: j, T5 ?
- $members_preferential = M('members_preferential');
0 Q5 U1 ~% T* G - //对应投资金额,
" v9 O, X! k" v$ o* X0 x0 r3 M - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
9 ]% Z0 G7 a2 \% z - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');! e' L, P& o9 Z8 G1 p, a& ~5 q
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
3 d# h& q9 T/ Y - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
. R6 |/ `. D W - if($save && $add && $add2){8 G$ a* z9 E, G3 T
- //事务提交
0 k+ b e$ k( a& {9 @6 ^0 M% N - M()->execute('commit');
/ \/ l1 {+ o! ]. ~5 @1 I2 [ - $data['msg']='恭喜你领取了'.$num.'元优惠券';/ k# f9 q- q! n% U. q
- }else{, _* ? l9 H! y. r- @* C- s8 c9 y
- //回滚6 b8 M" `4 ]' C3 S1 T% w, \
- M()->execute('rollback');
6 r( i' r7 K+ D8 H7 R7 G9 ~3 | - $data['msg']='未知错误!';
6 d3 O6 U0 C* t! X7 H$ a w - }
3 i- }+ ~2 s, _; r - }else{2 L G4 Y; v: R7 F; U5 l1 q3 m2 F( R
- $data['msg']='红包已领完,你来晚了!';
- d. A* c. ?3 ^ - }( f) B& |2 \$ H- d1 P
- M()->execute('UNLOCK TABLES');, P4 r' A4 p( _
- }$ I+ ?0 h) u+ e$ W# W
- }+ z& u4 P0 l- h$ ?$ R0 }" [# s' y
- }else{7 q! e! o) u+ p* `1 U& U
- $data['msg']='非法操作!';) \" s- O y0 B0 E* i$ {7 J# z' U
- }
+ i3 Z6 E' G- j0 ^' J - }else{) h0 J. ]3 K0 O* z, \6 y
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
7 O* c8 Y# a+ r- [* s/ R2 S j5 q - }% q* x! {8 V0 w: d
- }
7 _8 ~7 T' v w2 ~# Y - exit(json_encode($data));: `5 L* W$ W x; k- T
- }
复制代码 ; t0 s D6 `, ~) o9 n% y2 a
$ H+ @9 X. c- u* z8 }5 i |