|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
}* m# D! U+ P' T! K3 C+ R. ^- }/ [% RMysql中的锁语法:, Q) r _; |+ w4 \! d6 \) C6 M& }
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】& X, t" }/ g$ Q, o: j# j* U
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
, Y' U* |6 d$ L& L- b+ J0 ?& iWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞# Z" J+ h) w! C; w0 T1 I
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
) I# d. i+ C6 E, U5 Q Q文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。3 r, G6 M* U, p1 T) \
测试时,有个文件就行,叫什么名无所谓 总结:* V1 v: h0 Z6 Y; G3 K
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:3 T1 U7 X K, q4 z# l. N
1. 高并发下单时,减库存量时要加锁7 n' R$ ~7 X" A5 `2 P+ z3 o6 ^6 Y
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /** I0 L# C+ `' h% k) u* N6 k* l! [
- 模拟秒杀活动-- 商品100件
. w8 f P$ P% a8 c' ?' |* D - CREATE TABLE ta. \1 b, C5 _/ A$ ?3 {$ V* ?
- (/ `- c4 K8 J x3 b
- id int comment '模拟100件活动商品的数量'
; W5 g5 i; g0 I: ~* b+ b5 C. n - );
( l% M( q, a0 s3 Q9 q$ k - INSERT INTO ta VALUES(100);% n' G2 i2 u0 h
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
/ _ c) y$ G) u$ L- } - */ # d: i* Z0 K0 D) n$ s
-
; E. E8 J% |/ S) ^; ^! [ - // 关闭错误报告
- ~9 g- X1 I H% \. P - error_reporting(0);
' [; z% L' [" l2 L# f - # v% a# `- n/ B) }4 B6 Z1 ^
- $dbhost = 'localhost:3306'; // mysql服务器主机地址' W u9 i' p5 k/ p* ]+ {
- $dbuser = 'root'; // mysql用户名& k% ?6 h) t# V' r3 L5 ~8 _* C4 M
- $dbpass = 'root'; // mysql用户名密码! a' L; H U0 X; l
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
" Q* m) X8 z" R - if(! $conn ). |1 A; X' a" [
- {! I3 c- s4 U# [: h* p
- die('连接失败: ' . mysqli_error($conn));
6 ?$ O, B) m& R" t0 R: A8 w - }' X! R" `, [% J; Y+ N/ }5 p3 O+ o
- // 设置编码,防止中文乱码
3 K+ [4 D$ o E0 r0 J. Z2 ]! `" U - mysqli_query($conn , "set names utf8");
6 ^ S# `5 r' L% q- ^ - mysqli_select_db( $conn, 'temp' );
" d* F y7 F; X \4 D& n- P - $ E. _2 V7 b) P/ r8 m" A9 r
- # mysql 锁 8 E0 l- a$ {0 D$ u5 T
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
4 ?2 Y0 T1 ]/ D. i7 L4 \$ _. J$ l - $rs = mysqli_query($conn , 'SELECT id FROM a');
1 P) ~7 l& ^2 V& X7 } - $id = mysqli_result($rs, 0, 0);
! _' G. w* e, S- e - if($id > 0) 0 @; q9 J# H" e
- { 3 l/ c) B! |$ V- i& N5 y
- --$id;
/ N- J6 m2 A) v& G% @% Y+ _' T9 E: N - mysqli_query($conn , 'UPDATE a SET id='.$id);
# R* E& C# @1 s& K7 O - }
" }! H3 ^ i( t( N7 a7 M" k" c - * I. g0 M! N3 |. a9 N9 ]: e
- # mysql 解锁
. q. f9 [* b" [+ `" F0 x+ L. [ - mysqli_query($conn , 'UNLOCK TABLES');
. }' ?8 Y1 m. L5 E5 V - //查询解锁后的id值- J# y7 q- N& u
- // $res = mysqli_query($conn , 'SELECT id FROM ta');0 t, D2 {: L. n0 J
- // while($row = mysqli_fetch_assoc($res))5 g7 {$ f/ A1 u0 `4 U
- // {; E% P! K# |" k9 o
- // $id = $row['id'];3 R q! ?1 b6 U' I1 L1 o
- // }
+ [7 z3 J" i( A8 N8 S - // echo $id;
复制代码 % L7 O3 K8 \1 A* b; }! C+ C
5 [9 W' \ ^! g, E. b3 {) ]. k1 q. [: ^5 t! |" S3 F# F+ o( \* N
PHP文件锁示例: - /*
2 C( g+ e7 [( B - 模拟秒杀活动-- 商品100件
$ [+ c) u- n' b# D! u - CREATE TABLE ta. `. n/ Z* q" a7 W
- (
, z u# Q9 y5 t - id int comment '模拟100件活动商品的数量'& S" m3 R3 Z' Z5 {
- );
9 U/ d6 F; J% c- L7 G - INSERT INTO ta VALUES(100);
- a7 p& ]' Q* o - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
' C4 A$ R% w$ ~4 F+ D - */ 8 T, c2 J: w( \+ y4 r" b
- 2 j& B' N+ @: G5 G2 Q f
- // 关闭错误报告. k5 _4 S# A6 `: S s7 \* O
- error_reporting(0); # c0 @6 I; D1 n- ?
- 0 h! x/ [% w0 Z5 K4 C D
- $dbhost = 'localhost:3306'; // mysql服务器主机地址( {0 R4 B& V5 F+ S% F+ O. M
- $dbuser = 'root'; // mysql用户名
% ]. t# m$ S- [' {# N - $dbpass = 'root'; // mysql用户名密码3 A/ h- B( T: b. E9 F
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);: r$ T7 ], N8 {- ?
- if(! $conn )
% w3 c% y! p7 N - {
5 [! r% X" ]* Z* F) t - die('连接失败: ' . mysqli_error($conn));
% o: }7 K( n) g3 W: a; v( o. S - }
4 l+ e% M" D) i* w5 x) [- j/ R# z* b$ ? - // 设置编码,防止中文乱码
7 }/ _; L% Y* z% Z' B$ o- \) P4 n - mysqli_query($conn , "set names utf8");9 v' T* Y7 \9 I
- mysqli_select_db( $conn, 'temp' );
; m6 B) y5 c# d+ h+ E0 F7 B6 b/ Z - , X T/ {+ C' J# p% }/ [: R
- # php中的文件锁
( N& L. ]8 C/ r; L% f6 z9 d - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
7 p* d# f+ y- r$ w6 ] - flock($fp, LOCK_EX);// 排他锁
g L ^% e# @! c - 4 f8 l* ^/ V& _' s. W0 N' j
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
S8 i1 z& s \. I - while($row = mysqli_fetch_assoc($retval))5 [( C7 V* f) m$ n( V1 h1 L3 ^
- {
$ d5 o% W9 R1 V$ U1 Z3 B - $id = $row['id'];0 f# I! l! N. c& X# B% X) K: x
- }4 [" Q, D1 S1 k, x4 J
- * m7 F* ?8 k* f4 R* ?# p+ {
- if($id > 0)
6 ?( ]5 Z. ^- J- N7 P5 F* d - { & t: u5 o) ]% S, _* ^
- --$id;
- f" s8 _3 P6 A7 G- h/ n - mysqli_query($conn ,'UPDATE ta SET id='.$id); + u! W. C0 K7 m$ J _$ W6 d
- } 6 p0 r! [1 o! S' Q9 D- m/ k0 ~" ~
- # php的文件锁,释放锁 ' X5 l% P9 X# z3 u
- flock($fp, LOCK_UN); , n9 _( X& J2 [2 D7 G
- fclose($fp);
" ]4 R% m: }: ?' L) w G - , B0 `2 S: f1 U. s3 |( E
- // $res = mysqli_query($conn , 'SELECT id FROM ta');/ P9 d! \& b8 `# Y' V* l
- // while($row = mysqli_fetch_assoc($res))
% i8 c0 f9 [0 B - // {
y' ^# K, E* T/ s; [: A" G& V - // $id = $row['id'];( ` j. Q$ y. p, z# I z1 t; x7 c
- // }( s0 |8 P8 S2 E% p' V6 D2 {9 v
- // echo $id;
复制代码
; _( E3 K7 v9 {4 P# l4 A( ]5 u1 A
抢券活动实例: - public function envelopeSnatching(){
* F) Y) ?1 ^1 p9 H% Z0 k2 h - $lingqu = $_POST['type'];
* R3 f. K; m( N+ \3 J+ ` - $uid=session('u_id');//用户id) _/ p1 J) ^- p# G4 ?7 K1 g
- if(!$uid){0 k3 T1 M6 Q' p7 p- a
- $data['msg']='您没登录,请先登录!';6 f" P( ~$ K# g; K
- }else if(date('Y-m-d') != '2017-12-12'){! N# x1 k2 Q6 i6 u/ ~
- $data['msg']='不在活动时间内!';
# T% I- Y2 {" |: n6 G+ M3 p - }else{
) }6 f! p( ?" l - $hours=date('H');//当前小时数
/ K* q4 a3 D1 w5 q, `$ f - if($hours > '09' || $hours > '17'){& S5 N7 _+ o4 n& v9 x) _7 V
2 T/ Z/ E1 }* n3 P" n- if($lingqu == 1 || $lingqu ==2){//点击10点的
/ [4 g7 f; F# }8 E. E - if($lingqu == 1){7 |! ?& X Q ~& c# d" E( _6 C
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
, @+ r D% w1 x2 t% X - if($hours > '09'){7 ]$ ]( \8 }4 Z" {
- $num=mt_rand(25,28);//优惠券金额& L/ s4 b/ v- ]% u
- $id=1;2 a$ Y; b d7 o5 n) B1 a
- }6 m+ o# N( m2 |& M# c
- }else if($lingqu == 2){
6 \+ M* w0 i; h% J( E! D - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
m I) O5 I& g# m/ v - if($hours > '17'){
1 w. z- C, Y( O N* A: `3 G - $num=mt_rand(50,55);//优惠券金额
/ j+ c0 D7 L" A, y* d8 W - $id=2;3 j4 x0 L# a7 }; C/ \! ]( \
- }
8 s5 ^; h% ?8 B - }8 ~& R3 U) ^9 C7 D$ \
- if(!$id){
+ g/ Q: e) V' m& ?. |9 w - $data['msg']='时间还没到,晚点再来吧。';5 {+ b! _; C! ?+ z, ]3 y$ u
- }else{# R4 {* L9 _/ f9 }) U$ S1 |
- if($is_lingqu){2 a) t( N `' }4 e
- $data['msg']='你已经领取过了,留点给别人吧!';
0 ]+ M9 U7 U+ G7 ^3 `+ ~ - }else{
; P- [+ Y7 S. e( u2 M5 X - //锁表
! I" `2 D' Y+ `6 f - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');1 a& ]& V) m( X! R7 {) s$ u& W
- $active=M('active_num')->where(array('id'=>$id))->find();: I% e. z+ g0 {* g! N3 ^3 T' Y
- if($active > 0){
$ @# k0 N, L, x" j7 R' S - //开启事务
1 J$ ~. F5 p$ C5 \" ?2 w9 X - M()->execute('start transaction');
* f$ ?' j3 ^7 t - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
& w1 N$ E8 J7 C" B6 ]: N } - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));( d; L0 x {9 H7 l8 y
- $members_preferential = M('members_preferential');
9 k! `7 P, K9 D( H - //对应投资金额,
" ~/ p& f; e- B, H! h! I+ P0 q) | - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
! g* U: }) H/ J8 L) O' _$ U$ ? - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
5 L; J1 P' \/ ^; ^ - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间. A" V g1 d9 Y% A' M0 d _
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券" O* R+ N7 ^! A+ k% x, }1 V
- if($save && $add && $add2){" m9 Z# ?8 r2 K' T9 F
- //事务提交
& B: ~4 K- g8 y - M()->execute('commit');
- d# l) X- g9 A( M; D - $data['msg']='恭喜你领取了'.$num.'元优惠券';
- q; V" S: |' F O9 h/ a - }else{0 U# C3 |* G9 Y" M6 ~
- //回滚
. [/ Q9 L6 d, c2 |- p2 I: J - M()->execute('rollback');3 p5 `+ F5 n ~# F3 a k
- $data['msg']='未知错误!';
, G3 Y% t% V( N$ c( q - }
* b+ Q% J! o: U1 c8 o. e7 d- U @ - }else{& i0 f1 Z2 q3 ^4 j& x% Q
- $data['msg']='红包已领完,你来晚了!';
% ]8 v% f: ^- E+ f3 \) Z( E - }6 o- ^2 m( S( s# V
- M()->execute('UNLOCK TABLES');
+ G D% P( k: [/ X - }' E [' n b( b8 J1 n7 {
- }' Q* F# `! X7 j2 y% l
- }else{
& g, O5 A9 d; N9 z - $data['msg']='非法操作!';
0 d6 l; d! \) o/ k( R6 I - }
' p/ s8 G% W/ H6 ~4 |8 J* _! b: v F - }else{# j8 f0 ?& T; ]2 G' t3 Y; c9 S8 v
- $data['msg']='还没有到活动时间,请晚点再来哟!!';) E( w g6 W& i0 k$ P# W6 R0 U
- }$ Y* c* e, b. m2 U4 \1 L- W
- }
% y; e& E& n" A9 A - exit(json_encode($data));
# F0 Y& s3 W1 C0 {4 V( J* `1 H - }
复制代码
3 A1 i) \5 Q/ A2 j
4 q m' L& {" E, K) }4 y( A z+ d |