|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 7 |- a( e# l' s
Mysql中的锁语法:
# e1 m* a4 C9 _2 v) {* nLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】4 u* \5 n4 B9 o1 B" P7 `& A- m7 u; ?
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
' ~* c, _7 n# h& W6 r/ ~# c" N/ [Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞& x9 O' [# S) \
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :* H' s( @5 s2 O+ H
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
5 @; E! G) E W w- N测试时,有个文件就行,叫什么名无所谓 总结:! ~2 n* s6 s% {( Q/ ?' i
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
5 s" Z# y3 E! E! V. J# u: a1. 高并发下单时,减库存量时要加锁
- m6 t( J- y' x2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*5 b5 z2 h' _7 }3 n; B
- 模拟秒杀活动-- 商品100件
[ N9 @5 b; C, @, { - CREATE TABLE ta1 }$ A0 C9 I' F& E& S
- (/ ?6 p6 Z. v6 p* m& e
- id int comment '模拟100件活动商品的数量'
- d4 ^/ C6 o" `! p; I6 [ - );
$ T- V. q9 K$ Q/ C' t1 Q - INSERT INTO ta VALUES(100);
6 h& P* C( ^' X4 h* y - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
( y2 R% X) Z( y9 ? - */ 1 b' b! J7 _: b6 C2 q5 D. H
- % V! V' D# f/ x
- // 关闭错误报告( U8 U6 c3 }. }6 _% x( h5 N+ {1 a
- error_reporting(0);
, J$ v7 u. K0 m: T/ W9 Z
. \4 |8 T5 P& ?- $dbhost = 'localhost:3306'; // mysql服务器主机地址
6 T: x5 r! f3 r - $dbuser = 'root'; // mysql用户名4 W+ I* r7 P; f' F! X. b b, V
- $dbpass = 'root'; // mysql用户名密码7 T- e! S) l) X: q/ F# ?. a+ |
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
, g$ d6 R l& ~. e - if(! $conn )
0 R6 `4 Z- E, Q: C& C( o5 {8 P - {/ v1 c3 Z5 Z$ N
- die('连接失败: ' . mysqli_error($conn));
$ X9 u: t7 A1 ^. F+ u3 T - }
! V# z0 ~8 a) m, y* Q& i* U - // 设置编码,防止中文乱码8 e* O) H) p5 q, q6 T
- mysqli_query($conn , "set names utf8");
9 g) ^9 I/ N( Y: m! d) e, w - mysqli_select_db( $conn, 'temp' );
h0 j3 z' R2 n; V4 @
2 e0 e i) W! X& ?3 K2 J- # mysql 锁 * E# _6 t0 ?- R; L$ @0 \) R6 a! y
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
. W* n u9 f4 `4 A* d) f _ - $rs = mysqli_query($conn , 'SELECT id FROM a'); ! `# j7 E# @; h, d
- $id = mysqli_result($rs, 0, 0);
$ @2 ]% d6 T! p- ~+ k5 Y - if($id > 0)
R; C( c1 t. X0 _ - { 5 f6 Q: Y: _7 y% C) G
- --$id;
! X8 y1 Y. O- i1 ~8 {$ ] - mysqli_query($conn , 'UPDATE a SET id='.$id);
: p0 o8 k+ n5 _! |3 X) d3 J# o6 H - } 2 _2 Q* W% c' S) k. z7 Z# T0 J
) b% R1 X( Y1 A* j2 B- # mysql 解锁 : ~: k$ N+ _/ _9 f6 U' X* i
- mysqli_query($conn , 'UNLOCK TABLES');4 h" l. q2 h7 v6 h5 N2 m& ]
- //查询解锁后的id值
* U( i- i5 n# _+ ]# ]4 _, y" t# E - // $res = mysqli_query($conn , 'SELECT id FROM ta');
: \8 ^1 ^: I/ \( d3 ~( c% ^ - // while($row = mysqli_fetch_assoc($res)): _4 g+ r& F$ Y4 v
- // {5 C. s4 Q9 s5 w* t4 d, \
- // $id = $row['id'];5 B1 K& ` [0 Z5 R* m
- // } K& f1 p% ]4 E9 S4 O# \# I7 n
- // echo $id;
复制代码
* V" p1 b" r7 `0 N/ H& a: u
: I6 f0 D: t3 ^
( X8 C7 F7 u8 T. h# aPHP文件锁示例: - /*
: B' q2 k+ P$ \ D! i. \; g+ q* m - 模拟秒杀活动-- 商品100件9 @4 \& A1 H- a& n3 N( U# n
- CREATE TABLE ta
; d+ G. A0 h( D K6 X [ - (* b; ]( T0 Q" J# k
- id int comment '模拟100件活动商品的数量'
% g8 j' n+ Z6 O5 O* d f; s( }4 M - );" ~6 w$ f2 j# M
- INSERT INTO ta VALUES(100);/ j$ v. x; I* F; f/ }' N1 u c+ N% n |
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件* Z# `' y. Y! ]& u; R/ e& I% P
- */
4 r! y# V" [ j* f - * B7 {1 f" w: o, i: m+ ~) F' h
- // 关闭错误报告7 ^6 V8 Q7 ?" N7 e9 I' Y
- error_reporting(0); # n2 I3 {( T' ~6 D3 B- C+ Q
- # m: f% m- B+ P9 o2 s; g
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
8 E- f# y" j- }. Y3 m3 }1 ~ - $dbuser = 'root'; // mysql用户名% Z% H% f. O {7 k5 K
- $dbpass = 'root'; // mysql用户名密码* Y* u2 d* C( S) o' _: A
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);! x# t3 L$ f) v; L/ o* F [
- if(! $conn )
9 I/ u, O' i9 B1 m6 U, e - {9 [3 a2 Z+ ]; c: H8 g. t) y* j" R1 M
- die('连接失败: ' . mysqli_error($conn));
* Y, W$ x* E5 [7 h' _ - }, ^+ [, E' x2 _* G, y$ e& f
- // 设置编码,防止中文乱码
5 `3 k5 E$ O" a) p" [, g - mysqli_query($conn , "set names utf8");
1 c& {, m) ]' h& u8 r! \9 H - mysqli_select_db( $conn, 'temp' );
2 r4 Z2 D* J. N: L7 [
1 t, Q8 e0 \) G% C3 D& @# [- # php中的文件锁 1 P9 O/ l0 I- ~ s
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 3 H5 ] A9 R. g _5 [
- flock($fp, LOCK_EX);// 排他锁
; e4 U0 W$ g4 C% g7 }3 X8 d
3 c. w% z9 k( v- $retval = mysqli_query($conn ,'SELECT id FROM ta');
0 o* e/ o2 ~7 R( J# ^& m1 y4 b - while($row = mysqli_fetch_assoc($retval))
: ~; U! R' E+ Q% ` - {$ Q$ B8 e& K) J+ a( a5 p
- $id = $row['id'];9 [" v8 m, t: s
- }
, k( u1 I9 B/ L6 O M7 g: O -
0 b4 _/ J) t# Y& o# X - if($id > 0) N, n/ j' W( R, {$ t0 l0 O
- {
; y( \' P5 n9 p2 F, B% z" k - --$id; - G. I, z/ x. f, z- J
- mysqli_query($conn ,'UPDATE ta SET id='.$id); . Z* _4 {- J7 N& V& r) f5 n# t
- } 8 _4 A8 M8 R; V' U! A$ g
- # php的文件锁,释放锁
1 ]/ V9 [+ E2 @0 _ - flock($fp, LOCK_UN);
; U& G2 B1 k2 u, b6 |' v - fclose($fp);/ J0 B# Q; M" K5 o) V9 K
- 9 z1 j0 O* M: B, W# h
- // $res = mysqli_query($conn , 'SELECT id FROM ta');% Y* g6 B) \6 S$ h
- // while($row = mysqli_fetch_assoc($res)); k; C+ [1 }# E/ J5 x8 Q/ W
- // {
, m' o- z. B# Q! ?1 v2 S) l1 J - // $id = $row['id'];+ e- K4 Z6 D T6 C+ i( g
- // }' i* Q, f8 U( X0 u4 k9 x4 ^1 @
- // echo $id;
复制代码 ! M2 Q9 Q! @6 Q
. f6 R! A, P7 Q/ y3 C, ?+ x抢券活动实例: - public function envelopeSnatching(){
: z: ` o2 n& V$ Q1 V4 M - $lingqu = $_POST['type'];9 ?. T/ o; Y: w% j9 @% ^
- $uid=session('u_id');//用户id
- O$ m; ~$ q. u$ P0 t - if(!$uid){
6 ~4 ^( J$ \; J+ G; u! A - $data['msg']='您没登录,请先登录!';
; _/ s9 M& x4 b C$ { - }else if(date('Y-m-d') != '2017-12-12'){
* n; R2 Z* P5 {2 a! d - $data['msg']='不在活动时间内!';) H( Z& X% u2 {6 D& Q
- }else{9 a. P$ ?" Z' C2 h' _7 ~
- $hours=date('H');//当前小时数1 O/ ]' A. G. ]5 @; n. w
- if($hours > '09' || $hours > '17'){; T& o5 c, [% d
& [" u3 o2 q d9 s1 ]6 L9 H3 |" j4 s- if($lingqu == 1 || $lingqu ==2){//点击10点的. P* c b& r0 W: a* K6 e2 }
- if($lingqu == 1){$ n, S9 E e- h T' u7 w
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过' U6 O% L* h, ?! t8 y6 m
- if($hours > '09'){
8 l: V' Z( U3 `( k, S E z- K - $num=mt_rand(25,28);//优惠券金额 ]2 x. I0 v5 h8 c( U. F. K* H
- $id=1;
{8 N' ]$ |' v$ n# b) o - }
0 ]) Z5 r( }0 ]: ?2 \$ U" p/ W1 n! Z* ^ - }else if($lingqu == 2){% E3 [* g2 E5 }' Y4 y
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
. f1 o0 Z- m) | g9 k$ J- C0 q - if($hours > '17'){
6 Z; J; r' M0 @ - $num=mt_rand(50,55);//优惠券金额
- A, ~# R7 _3 ^/ E% M - $id=2;
% Q" z, t: |- e - }
* x8 J% `) Z; O. [9 m - }
) o2 z, ], e+ @4 w- L& R - if(!$id){$ e0 s/ n3 M7 q7 y
- $data['msg']='时间还没到,晚点再来吧。';
$ H+ G* v7 u8 f: O1 v/ \) @4 A g( C - }else{
, e7 g) S( Y, T' d* u9 L5 D - if($is_lingqu){
' i& I G# m: o* |/ S' q5 t - $data['msg']='你已经领取过了,留点给别人吧!';: l$ V' [' ~+ S
- }else{4 F2 i# A7 C7 s0 A
- //锁表
1 R/ d+ A, P: l, D - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
( }9 r7 p7 R: q4 G4 U* q - $active=M('active_num')->where(array('id'=>$id))->find(); H$ N" f: w/ T7 G8 d) ^8 M9 k2 L
- if($active > 0){1 F. _7 s4 U1 v6 @5 x2 k
- //开启事务
" S- R9 O. J# D2 l - M()->execute('start transaction');
% u# i' z/ h' R! D, T( ^ - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));# A5 B& d w% g3 R/ f [( p# i
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));& C% K0 Q7 m8 O' v4 C* H
- $members_preferential = M('members_preferential');( \: K' N& Y6 L5 M: ], d+ H7 R
- //对应投资金额,& P9 C) ^2 O0 ~0 u: y' k/ Z$ E. t
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));, S0 ]- M7 I9 R" t5 k/ M' T
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');! Y$ t; C$ V z% |3 c5 Y0 g5 g
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间; G+ H( N! T3 v
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
: X& g4 p% c( w - if($save && $add && $add2){5 S! C4 ]. Y& u6 W
- //事务提交- I) G; i: g5 P
- M()->execute('commit');
1 i$ @# `3 `2 b5 @ - $data['msg']='恭喜你领取了'.$num.'元优惠券';8 M+ f" Q7 j+ [% V& _3 f
- }else{& H+ R: W; `; R9 W5 ]7 P
- //回滚
" `" v2 h3 Y6 q6 N$ O - M()->execute('rollback');& T% [3 a4 d, x* y; I: e
- $data['msg']='未知错误!';
/ c9 f- ~: C8 D( S q9 y! n1 h: y - }! H/ C i+ }4 f/ e; L! Y% M) U
- }else{
, r; n4 O8 p: Y/ d1 d7 i - $data['msg']='红包已领完,你来晚了!';
) R; B7 q d& s" s1 t" @; X - }) o8 J9 w0 T, h* F( U
- M()->execute('UNLOCK TABLES');
1 c X! R! ^' o5 a - }6 w( ?" J9 t/ u8 S* l
- } i7 N( I( N9 Z" C( \
- }else{( {2 K2 V5 y% t
- $data['msg']='非法操作!';
! N5 D$ g. ]+ x0 _" d" H$ _ - }* h# K6 R. E9 @7 g# Q
- }else{
; `: Z! }+ D1 t4 `/ v( e - $data['msg']='还没有到活动时间,请晚点再来哟!!';" ]2 {( X. Y# T5 k& C2 ~% e) ~
- }
6 {6 l7 u# h' X8 V0 G6 U( z - }
1 l; D/ G7 \* N - exit(json_encode($data));
; y7 w2 ~8 g- c p+ l4 Y - }
复制代码 % H! }: b }& H/ I
& W2 l4 f2 C% L$ O9 l$ Z0 I |