模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
! z E) A& R: c# ^/ EMysql中的锁语法:
6 F' ]% x6 d. {- rLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】8 R* W* Q+ @9 w' r" y+ |2 L
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
0 E0 a2 A( a' Q$ `8 X3 c/ \Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
) w4 Q7 Q9 A, x0 o4 P注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
" c5 F% e( t# B5 i& r- s' n文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
( @1 y/ [9 X0 t+ C& H, J测试时,有个文件就行,叫什么名无所谓 总结:
+ F" I2 ~$ W. N K7 y/ A7 N( |项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:. F7 c4 y8 g0 g7 ^6 M8 l4 ]' n
1. 高并发下单时,减库存量时要加锁
y9 B+ F/ m- S N7 {: x$ z2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*$ b$ A" r9 Z, s2 O
- 模拟秒杀活动-- 商品100件
K; h7 f- _" d+ f; M& C - CREATE TABLE ta
- z4 V. t( e: W6 U. @( G0 O1 R - (
. d, Q& h0 n4 r, C8 t - id int comment '模拟100件活动商品的数量'
6 w' s9 d- a5 m. r$ L& Y6 ~ - );
% I( Q1 r; p9 i4 N# Z! d. d - INSERT INTO ta VALUES(100);
+ }/ l7 k1 q; c9 e& `1 j - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
1 g7 h* ~+ }/ p# j* O+ @ - */ . b( E( x* ~) U5 Z% b0 m: M
- 9 k* @& F1 b" r+ Q& j
- // 关闭错误报告
$ l) G/ c9 O% d9 |9 R/ H1 l - error_reporting(0);
8 L7 d% I/ E' A8 H" ] - ; P) I/ H0 m) w4 ?/ S! y/ @' e: `
- $dbhost = 'localhost:3306'; // mysql服务器主机地址; o4 ]7 g' G) F5 d9 d& F
- $dbuser = 'root'; // mysql用户名
+ c( x4 R8 P3 ]4 i, {5 \ - $dbpass = 'root'; // mysql用户名密码
1 `6 N5 t& A# b - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);' ~2 e3 L9 i' V; {1 m
- if(! $conn )
`' E' g# |& X; F) H* a9 w$ S, U1 [5 { - {" `8 |9 \3 b" K0 y5 a
- die('连接失败: ' . mysqli_error($conn));
/ u6 m' c+ o* N* x - }$ [! M, u$ _) L8 \3 T
- // 设置编码,防止中文乱码
% N" k2 c# L0 k& A8 E - mysqli_query($conn , "set names utf8");
( V- X1 I0 \4 M - mysqli_select_db( $conn, 'temp' ); 8 P8 `3 x1 f$ w, x; e D: v7 u& p4 S
- ! R( z4 a) h4 V& h+ s2 z
- # mysql 锁
- A# \& ~' o( W; R3 b; Y9 l. h7 A5 ` - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
% D& I) L* P) D- a. [1 W- r' c( Y; s - $rs = mysqli_query($conn , 'SELECT id FROM a'); $ m/ b6 g C9 N; K. x" }' V7 O. J
- $id = mysqli_result($rs, 0, 0);
' ~/ O, o4 x [! A5 S S8 ? - if($id > 0) / {/ [* I" s: O
- {
( V: I' j) q2 n: C - --$id; 6 T+ J$ \# p3 Y0 n) P; y, g
- mysqli_query($conn , 'UPDATE a SET id='.$id); 8 q+ [2 S2 U( E" _3 M
- } ! \ a( ^, t: Y$ q
- b. H8 ~" b; N0 Z% y- # mysql 解锁
/ X5 ^& A1 f4 j' n' W - mysqli_query($conn , 'UNLOCK TABLES');
- B! k B6 U C. W3 E - //查询解锁后的id值
0 m7 ?6 f. A1 S0 w - // $res = mysqli_query($conn , 'SELECT id FROM ta');: \- d2 a7 q; S A8 F2 Q6 f
- // while($row = mysqli_fetch_assoc($res))
; ]# y( f K+ u) V: w9 k - // {6 A3 B- q# t6 {# `" a0 J& D
- // $id = $row['id'];
( M: g. ]! K# T% F$ j: P - // }
: _0 s6 ^) K8 w5 z! N, S9 Q- u - // echo $id;
复制代码
2 m8 H% ]& P, Z( n
. M, D- S; }8 }5 X0 _1 W3 A/ u" ^6 a# H
PHP文件锁示例: - /*
; T: T! ^2 N6 s# o8 C - 模拟秒杀活动-- 商品100件/ S1 e& X7 j" x9 }
- CREATE TABLE ta/ j, y. q# P. O
- (9 h/ T$ ^3 A' W: _, N
- id int comment '模拟100件活动商品的数量'
% y8 u5 f( ]4 _; }: B, @2 | - );* o! c. G4 G0 C* Z0 p
- INSERT INTO ta VALUES(100);
9 T' n3 q3 Z9 J6 b4 h! f) ^) K - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件: F: i5 ?0 }. B0 h& C
- */ 9 m! Z" h5 ]! \- w( `. q0 }: h
-
4 i9 l* E3 X. `5 {- \ - // 关闭错误报告7 ^& w( r% q4 j$ R0 j* [
- error_reporting(0); , i9 e6 X% s: o
- * X. I0 Q6 v7 l9 L0 C! G; H
- $dbhost = 'localhost:3306'; // mysql服务器主机地址( t) [7 q" ^- U
- $dbuser = 'root'; // mysql用户名% L/ z3 x; Q1 |" C
- $dbpass = 'root'; // mysql用户名密码0 G/ G7 L6 P* g8 V
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);5 r' Y# J* s- w7 g8 M
- if(! $conn )
( z7 d% e" U) M, N4 [ - {' K; G) `- V7 S3 u& B
- die('连接失败: ' . mysqli_error($conn));+ I7 N* m/ j1 o8 ^
- }
# U0 a8 C2 n& m - // 设置编码,防止中文乱码
" |5 G8 e1 A! L* z- d9 [: w/ S - mysqli_query($conn , "set names utf8");) q- |. L& n; N/ P: N
- mysqli_select_db( $conn, 'temp' );
7 k, d+ N2 ?) i8 @' k
6 Q; V% G# h! u% g% `: L& W- # php中的文件锁 ( ?- a, u( g+ q2 J
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 + o3 [$ \/ X) n
- flock($fp, LOCK_EX);// 排他锁
6 j* a! d8 t' Z1 { - * a1 T& k* {. }2 D; G0 z
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 4 t. C$ m8 G J$ {
- while($row = mysqli_fetch_assoc($retval))
3 q: }$ B7 Y9 V1 U$ ] u y - {# t6 t" N3 V; F& l: F' `
- $id = $row['id'];
" @3 f' c+ H6 P3 ?8 s; B- V3 F& G, K' O - }: @8 i, U; c/ z$ q
- ' l% ^- C5 d1 ^5 S1 s& z
- if($id > 0)
0 F8 M+ [; W1 T/ v7 p: G - { 8 d, z% s; d6 s* D4 Q
- --$id;
3 `: |0 D! D# B6 `! S/ W - mysqli_query($conn ,'UPDATE ta SET id='.$id);
$ a( f" b# o5 f% C+ \ - }
# O" B1 L# ?( D$ Z+ V; W4 }% ?( i - # php的文件锁,释放锁
; `$ |7 ^& U$ `3 h, M' k - flock($fp, LOCK_UN);
, W. j; y9 _3 z, Q2 I* [/ @1 D$ F0 [ - fclose($fp);0 `4 j# x! Q# O( h# s
- # d! o: l; t0 R6 g) _
- // $res = mysqli_query($conn , 'SELECT id FROM ta');5 n e9 Z# K- c7 p* m
- // while($row = mysqli_fetch_assoc($res))- d# K7 z* m2 ?# t$ V
- // {* I7 z" u3 W' n1 h& J# i0 C
- // $id = $row['id'];+ K$ C$ t' J" d5 i8 j6 v
- // }
! h5 C% [" r" U- M3 g4 g - // echo $id;
复制代码 - H* p Y( Z6 |
! z- I, B4 f& |. e/ e: o
抢券活动实例: - public function envelopeSnatching(){2 Z! P/ L4 A0 \; z3 ?4 h
- $lingqu = $_POST['type'];
9 J5 ~0 x0 g$ J' h- o- g - $uid=session('u_id');//用户id8 H. h1 N! ]9 Y; F% q- Z( o0 H9 Y
- if(!$uid){- N; q, R: W% A0 f$ S
- $data['msg']='您没登录,请先登录!';
) O* p$ m& W: |9 c, q" k# J - }else if(date('Y-m-d') != '2017-12-12'){
% o3 Q4 h$ l$ B( C - $data['msg']='不在活动时间内!';
7 r2 J B1 T k ~ - }else{
; m8 N2 v, w3 G5 o2 E3 H - $hours=date('H');//当前小时数4 J, ^$ Q2 z' A$ Q2 N) @9 S) ~- B4 ?, Z
- if($hours > '09' || $hours > '17'){
% I6 Y1 n* B& @& r' Z P' D8 f
2 J! v' d" u+ K6 F* I* }# {- if($lingqu == 1 || $lingqu ==2){//点击10点的, {5 o5 g) w0 z7 c- G( m5 l% _
- if($lingqu == 1){. D# g; c+ @3 f. o" X7 Q
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过9 u. I; L0 Y+ V2 o1 b
- if($hours > '09'){3 w8 O3 @0 U8 f/ ^4 j$ h
- $num=mt_rand(25,28);//优惠券金额
9 K; e; M. C- g: w9 F; L/ I - $id=1;
9 b% t4 D- I" Y8 N* e$ |# p% N6 V - }3 E4 _% m! ?1 M9 S+ d# u
- }else if($lingqu == 2){
4 K( g0 Q3 c% S. p - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();7 q9 ]/ y6 `1 L! Z
- if($hours > '17'){' e7 m) Y/ n" p
- $num=mt_rand(50,55);//优惠券金额
, ?2 l( Y* _3 l; J6 Q - $id=2;
3 l. C8 i: B' }+ J9 K - }
( n) P3 h G- C9 b3 z3 q - }
* v7 R5 |: S5 g - if(!$id){! W. u) Y p# c( Q
- $data['msg']='时间还没到,晚点再来吧。';
) U9 R) |( ?) y0 r - }else{$ K- ?: C7 k9 C9 r; O
- if($is_lingqu){
, w7 a$ w' P! s0 k - $data['msg']='你已经领取过了,留点给别人吧!';* P$ H( I" I% K
- }else{( ~3 ?" c' K: d/ k, [
- //锁表
$ K- P: y4 \, G( d7 T3 V( H - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');; S$ V. f, K5 h* y: u/ {5 d+ Q4 H
- $active=M('active_num')->where(array('id'=>$id))->find();# ~3 s. M/ {1 `9 I+ c v" h
- if($active > 0){+ _% g8 u1 w# E" w1 B/ w2 J& }6 ?7 P
- //开启事务- C4 p8 X; i3 r. L
- M()->execute('start transaction');5 e5 f" }# x. A
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));( Y% [: q+ p. `6 s/ e- A/ n
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));6 d* _8 j% \7 r/ P* [. k0 f$ k
- $members_preferential = M('members_preferential');$ _1 K. M6 c4 B7 W& g$ O
- //对应投资金额," M7 @$ [6 B. ]! r7 {8 Y/ _! g
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
: }9 \+ f, I/ y; ?5 h* J( C - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');' Q* T& g2 B, B% C+ T
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
! f7 a7 V. A' u& m- o' ?6 u - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
* e% U; w2 Y) \* m+ [0 y; z8 s& ]9 d - if($save && $add && $add2){1 S B& X5 f' k5 E( F% v1 i8 L
- //事务提交( h G) b" S, Z9 ? _" H$ O# E
- M()->execute('commit');
# z$ D+ R+ ?( b- z! U$ Q& o - $data['msg']='恭喜你领取了'.$num.'元优惠券'; x& p2 {# i# ?+ T' B" Z
- }else{
* F8 ~4 v+ A# E) ]+ } - //回滚" c( V5 E0 Q7 T$ \6 X
- M()->execute('rollback');
- A; g4 ~6 i/ |# w+ { - $data['msg']='未知错误!';
7 N7 _/ K% P, v: D9 S2 p - }" \, D0 T1 Z" H, w
- }else{
# u7 b: x3 X1 ]9 S - $data['msg']='红包已领完,你来晚了!';
; ]+ [) g" z& X8 t5 O9 t - }5 [& B# R4 p) `& j9 Q
- M()->execute('UNLOCK TABLES');. \0 I& e$ L, e& C& }/ m
- }8 \- \0 a7 P4 Q$ K
- }9 ^8 h. L' o) y5 `- c1 m2 [( k
- }else{
6 e r; ]$ P6 I9 L9 Y - $data['msg']='非法操作!';
. l8 P5 {2 [7 P' j. i x - }
" D( G `7 U# @' }3 h - }else{
1 K* q1 X+ s& y6 V8 F7 Q0 B# i) m - $data['msg']='还没有到活动时间,请晚点再来哟!!';
# f+ _- w7 v, L: V. m; { - }) x! |. u, c9 \
- }
$ j; u/ F; N# n, b: g% C* B - exit(json_encode($data));
2 N7 o: m: ^; l9 Y$ r" e - }
复制代码 + Y1 K( o$ D& w8 m
* W4 W/ B0 ^5 T( F+ T6 |( p |