|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
, `9 d, A& f. ^Mysql中的锁语法:
3 G. ^5 r. ^$ | b* H6 XLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】& D/ y6 O. d' @, p
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表' K3 p1 F& y. N" [. u1 c; U
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
+ S$ N! e5 Z; N C( {注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
+ g+ k \# ?6 V$ U6 ^文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。2 `+ n9 U H% k1 G( W1 n
测试时,有个文件就行,叫什么名无所谓 总结:: X2 U% P( u$ J6 ~. s
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:$ A9 o% \: L9 G" k
1. 高并发下单时,减库存量时要加锁
, V6 \! u7 C9 J8 R4 F8 ~2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
7 R: P7 M+ {* u* O - 模拟秒杀活动-- 商品100件8 y0 x! @* |& h1 y; Y
- CREATE TABLE ta1 [' P. B* F0 b' d" u
- (9 w# N6 j3 K( p( C9 |0 ^
- id int comment '模拟100件活动商品的数量'- v1 {% J: O4 o# C
- );
$ k& H7 T6 O5 h - INSERT INTO ta VALUES(100);* N' T9 l0 v3 E, @& `
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件4 {" a4 ?, L, u/ O
- */
# q% z9 O* F+ M- @$ ` - 1 Q$ a9 ?# T- q: B$ j
- // 关闭错误报告) r8 ?2 y/ s3 V
- error_reporting(0); ! `# J0 L: w4 b6 W6 o
- 0 g) u+ f9 V& r
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
5 J a) n9 F( s' p8 K+ W, K - $dbuser = 'root'; // mysql用户名/ j3 {# \9 [8 k/ h
- $dbpass = 'root'; // mysql用户名密码/ ~8 D1 K/ T! {/ R# h9 {- E' K5 k2 O
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);0 [- F% w9 [; j4 y4 o3 z
- if(! $conn )% |: e( K1 e. g x3 L
- {
! j; K. X: ^8 ]: }4 t2 r9 N) f - die('连接失败: ' . mysqli_error($conn));
/ X+ o( C) j. L4 \+ q1 { - }0 g. {. b; f; s. h1 A4 [
- // 设置编码,防止中文乱码; A4 [3 ~& G0 C5 F6 J. x
- mysqli_query($conn , "set names utf8");% v7 I; o1 _5 t2 R
- mysqli_select_db( $conn, 'temp' );
2 o$ `$ L' N L2 I+ Z, y! G3 J2 V2 A
) U/ X% r7 `, U- # mysql 锁
' u( c2 c8 O8 H ?) C1 y - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
* K& p. `# J4 M - $rs = mysqli_query($conn , 'SELECT id FROM a'); 0 [" R- ~' |: N; f9 Z. ]
- $id = mysqli_result($rs, 0, 0); 6 f; U& G o" H; V
- if($id > 0)
5 m- W! |4 l( h - {
: v# {( i% @% p# H; E) J - --$id; 9 D8 g4 w6 G. B6 ?$ F* J3 W
- mysqli_query($conn , 'UPDATE a SET id='.$id);
4 ?2 w. ]7 n" C" t) F# H8 c4 W0 N - } 5 B- _) |5 r# r! J3 D* q* G
- 7 U! _& |% f% \9 D3 n$ B ^
- # mysql 解锁 ) N" y% x9 P" s5 g" o
- mysqli_query($conn , 'UNLOCK TABLES');8 {& X$ v9 s. c& T' c- x
- //查询解锁后的id值
3 X0 u( V! C5 C5 P4 D( h+ H$ U! | - // $res = mysqli_query($conn , 'SELECT id FROM ta');
+ i9 q2 [, c# s9 t* |3 T - // while($row = mysqli_fetch_assoc($res))
/ L! H `3 u! c( _ ~ - // {
" ?' p: t/ {: b6 u, l/ S) I- R - // $id = $row['id'];
8 Y! H+ _0 Z+ v! B4 `0 F - // }7 p/ M4 u9 g9 l
- // echo $id;
复制代码
1 d2 z3 j1 N. m; X* U, A. K( W) i% \3 u, X k
- }4 y* _7 [) G5 E( t' I4 E
PHP文件锁示例: - /*! x- `2 h! L5 x0 A0 p
- 模拟秒杀活动-- 商品100件
% F2 ~1 |7 O. o) s) i - CREATE TABLE ta0 I1 ~, _! V! M. R1 Q; j
- ($ R/ n9 N, ?: C4 m
- id int comment '模拟100件活动商品的数量'
* _& T1 E: Y! `( W - );8 ^" O6 t; E6 {0 w. W% o5 k
- INSERT INTO ta VALUES(100);
3 V; r( o/ W7 O - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件% I/ y6 v6 c% N+ m0 a
- */ 1 f" l* J9 P# }2 u3 w
- ' }! j2 ^+ r# z: D, ]
- // 关闭错误报告
* v' k" e# b2 |9 S9 D& n" ^0 y3 \ - error_reporting(0);
i2 D/ n5 K1 y \& |, b - : Z5 x6 L% H/ h* h; l# Y
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
k' w% S; v( G4 p o1 n9 S" R& V' K h - $dbuser = 'root'; // mysql用户名. K2 Z- N% n) G$ Q( v3 q5 {
- $dbpass = 'root'; // mysql用户名密码
7 N$ h$ [; W* C+ l0 m9 U& O - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
% N7 B+ ^. `6 j8 X2 [5 r. d - if(! $conn )
. r+ J6 b! j4 Y; V - {/ ]& P0 M; C$ ` o: @& J
- die('连接失败: ' . mysqli_error($conn));
) D n* ~( n. R+ L* w2 L+ x' i - }1 l& _7 \; i: [/ N4 R
- // 设置编码,防止中文乱码2 a9 u8 E1 }8 c6 H. k
- mysqli_query($conn , "set names utf8");( H/ ^( e e9 v
- mysqli_select_db( $conn, 'temp' );
4 S* n* R% ]. s) L) G2 h4 L3 `
+ \3 _7 b+ Q7 Q! z- # php中的文件锁
8 u7 O: T3 T' N; t$ ]1 o$ t- ~; u) H - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 + O% p) i( z+ E% w/ X
- flock($fp, LOCK_EX);// 排他锁 ) t/ w* t4 [: ^0 r
9 x5 o3 D5 o/ Y% e: N- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 8 \/ A% x( H+ q7 @; c
- while($row = mysqli_fetch_assoc($retval))
. v8 _( ]# m! }9 x - {
6 t5 n& U. D# U/ T2 I* \ - $id = $row['id'];# a3 q5 y2 `; u4 {/ B; V8 w( Q2 y
- }5 O# k1 R! w; e$ z u( D
- 5 b# G, G3 y; i( I5 ?6 u6 _4 A
- if($id > 0) . e( k! x/ m' v9 G* i* X" f
- {
* s6 D+ V! `" N N e$ T% I0 `7 n% _ - --$id;
) s/ J1 K1 O5 S9 `# e* x" } - mysqli_query($conn ,'UPDATE ta SET id='.$id);
% Z" U/ ~6 c6 |1 N - } $ G) l _- d2 A: L- n% @- d
- # php的文件锁,释放锁
' a) h0 v% V" p! K/ b - flock($fp, LOCK_UN);
0 O/ z) o4 ~$ G& n - fclose($fp);4 w' F A ?0 S4 m* p* o
8 P* c7 p$ ?' c2 y7 X7 Q# B5 K& X- // $res = mysqli_query($conn , 'SELECT id FROM ta');
. x* x e# d4 I" A, J& A4 v - // while($row = mysqli_fetch_assoc($res))4 q; t2 |- I r; D/ t% R
- // {
2 w0 d% i2 c: T: M - // $id = $row['id'];7 j g0 I0 s; F, X
- // }1 n9 [5 w7 I6 i# W i( g
- // echo $id;
复制代码
; x/ c6 \8 n, i. t" E
9 s4 L" m% [8 [抢券活动实例: - public function envelopeSnatching(){, @/ e6 I( w, b K' X
- $lingqu = $_POST['type'];( [8 @, l# ]; r- q. H
- $uid=session('u_id');//用户id
; R1 ~6 y$ B \/ V* P - if(!$uid){- a# T# a# h' |( y4 X. q
- $data['msg']='您没登录,请先登录!';, {3 S( U. {, `( D; u
- }else if(date('Y-m-d') != '2017-12-12'){; s3 l. |6 B* W2 y. U& i
- $data['msg']='不在活动时间内!';
" o' _0 H. m, m) V - }else{
6 i1 F% _! N4 X) x% { - $hours=date('H');//当前小时数6 D" y, W, @7 M' a2 l ?
- if($hours > '09' || $hours > '17'){
6 _ V5 p( E9 e+ I; y, { - 9 M; f: u: D2 a2 A6 I
- if($lingqu == 1 || $lingqu ==2){//点击10点的3 G$ J* U& D/ b& [; F
- if($lingqu == 1){
$ p% P7 k ~" [! }/ j5 {' b# K - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过" N' H* j1 ^1 N7 Z& g( T# S% D
- if($hours > '09'){/ _$ o* p7 }* C! f
- $num=mt_rand(25,28);//优惠券金额' }2 y$ ~9 I' U) I6 w& P6 j4 Y# M
- $id=1;* n6 N- w! O, H0 C! ~. _
- }
& O3 V. Y8 [2 P! a - }else if($lingqu == 2){$ }) G1 k/ z: M. h" i
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();1 D, `* d/ w. B9 ]1 p0 \ w( @
- if($hours > '17'){: ~8 @: M2 V- u2 ~9 \
- $num=mt_rand(50,55);//优惠券金额) f/ {% r8 v" [5 _; g% Z- B
- $id=2;2 p4 |+ [( [" H' C2 ?/ t' `7 }0 X( Y
- }0 S. R% } W# \+ m' `; t
- }' y* X3 g7 [2 O
- if(!$id){; A. F w0 G- ~4 X0 J) [) R" J
- $data['msg']='时间还没到,晚点再来吧。';* K8 T0 Z; ?. W7 q) @
- }else{7 d5 E/ M4 N' G0 ]2 ]7 U
- if($is_lingqu){& P+ J; V5 r+ Z# j$ h2 a" K
- $data['msg']='你已经领取过了,留点给别人吧!';
6 F5 B, J( x; D2 P" O - }else{
4 O6 a9 u3 K' b* c( E: x+ M# N' K$ h - //锁表
) H- _$ x6 R# e; P* j( r. R - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');: @- J; L& o7 g$ h/ f8 ~* _3 }
- $active=M('active_num')->where(array('id'=>$id))->find();
: l3 s+ b( j6 ?% _) |' L$ P - if($active > 0){
# l* |8 d) @+ d7 y( ?8 M - //开启事务
0 o$ z. b0 x) b7 ] - M()->execute('start transaction');
\# d6 Q; k9 M! }- c - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
9 \4 x+ |; d% M% V t' C# m - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
2 B( v9 S# H% J: r - $members_preferential = M('members_preferential');
7 A. G% x- z q$ i: I - //对应投资金额,
6 H. P2 m& d6 P) ^! Q - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));) W$ ]2 B* l: Y6 h
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY'); a$ l' {/ j& s: g- m' w" i
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
8 z" ^8 ^+ V [ - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券3 `) G5 P' `5 f4 q4 N* Z
- if($save && $add && $add2){
4 g8 o5 X( D" W( E6 s' T8 L - //事务提交
Z; E# f8 Q0 s. ?5 c7 d+ M& r/ O - M()->execute('commit');& D- c4 ]! u# @5 k% R& P& |, E1 r% U
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
5 C/ C7 S6 E5 ? - }else{2 m# T' O8 R9 x. _8 l& r% k/ _
- //回滚% G3 k7 ]& V9 f
- M()->execute('rollback');: q0 K/ Y& P% S4 K% i2 P( b- _
- $data['msg']='未知错误!';
& C( w1 ~! W9 j - }
6 ^& I2 O! l. _. O {, l - }else{
" H* o: q5 l1 ?6 z( O2 y6 n7 z - $data['msg']='红包已领完,你来晚了!';3 M6 A7 M5 j. @ }
- }
M2 ?% r0 @ R7 z: n - M()->execute('UNLOCK TABLES');5 b0 `. n. D, Q* I
- }. c) |1 s% `3 N* y- S/ j9 N
- }
s: e& H W0 E/ E) `) v - }else{
2 {- r& s& ?3 _ B/ U7 M - $data['msg']='非法操作!';
* z& b) o+ `2 l3 _$ v! v - }
3 _: ]9 l5 c& A5 O+ k- f2 r - }else{" e- Y% v" y9 n$ M0 g
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
! `; ^6 L A1 T" A' n4 B+ O - }' ^7 m4 I$ x7 m( I
- }2 Z. u8 _3 n8 O/ ^
- exit(json_encode($data));; W% U" \5 P, i$ V0 ]+ n
- }
复制代码 * J4 V/ D8 L1 v; ~* c# {! R' i/ m6 Z
' ^- ~5 z" z7 G# d" ~, H3 ^ |