|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 5 e+ a) n, p/ F& Q% |( c
Mysql中的锁语法:: M9 g7 j" X; Z7 m0 K7 l1 @
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
/ t3 a+ P, ]1 n' n( S8 E% J9 }UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
5 {! e" K) Q- YWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞2 d/ P# x y0 W3 Y$ e
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
5 j) A0 E5 @5 g, s% `' U w文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。( k- K1 S( c6 U& J( c0 {
测试时,有个文件就行,叫什么名无所谓 总结:' D, g5 v+ u! v' k, H
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景: w+ B( q+ G3 t4 t
1. 高并发下单时,减库存量时要加锁+ U1 \" z) z7 @" D* c4 _
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*5 I8 x- i6 k$ x& Z
- 模拟秒杀活动-- 商品100件0 Z( F$ s: B* J9 W3 G2 S+ C8 i
- CREATE TABLE ta
; l' p$ A/ I Q' B+ D7 n% @ - (
3 T2 D* W( q3 Y3 _5 g8 y - id int comment '模拟100件活动商品的数量'
0 |2 `& _' Q9 F: x: d! t - );4 _$ V, k! o" n N
- INSERT INTO ta VALUES(100);+ O, u z! |5 Q7 k8 g
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件6 l. ?8 o# j) Y: n7 G0 x
- */
( P5 y% _) A0 E( V - 8 H+ r u1 S; E# U4 P7 V0 Z
- // 关闭错误报告" U3 v& T5 {4 a
- error_reporting(0); . K' @# P4 s' Y% u0 g7 d' f4 M5 T
- J9 y( R1 Y8 e. \ q
- $dbhost = 'localhost:3306'; // mysql服务器主机地址/ E8 S3 _7 j8 Y, u1 `' R; u% C- Z. {
- $dbuser = 'root'; // mysql用户名
' |+ K- Z k3 S0 `5 [6 Y% Z- ^* n - $dbpass = 'root'; // mysql用户名密码( D. Y8 C- d) y, L/ [8 b
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);% M5 b D# m/ S$ ~$ a; V8 A6 q6 G
- if(! $conn )7 [4 a0 f! w0 P# Z2 q, o
- {0 c! o* K2 q* M/ q1 @5 |
- die('连接失败: ' . mysqli_error($conn));
% z9 {* a- ~8 w' Z% P) H! G- k) G - }
+ a5 s1 K/ o3 P5 [, w, r2 ]) S& m) j - // 设置编码,防止中文乱码
; U1 I8 {6 i3 X0 F) u& s - mysqli_query($conn , "set names utf8");3 G" [8 B4 H5 s/ i( ?
- mysqli_select_db( $conn, 'temp' );
" x# v: ]5 @0 Y4 u" E - 7 E: A9 x p/ U3 E% x- {
- # mysql 锁 : X9 S' f) \, ^9 v7 P/ ^
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 ! M3 }( L1 n& n
- $rs = mysqli_query($conn , 'SELECT id FROM a'); 4 K; m. B6 V' l& C; H) j$ T
- $id = mysqli_result($rs, 0, 0);
$ o1 I [* Q' O4 l( J - if($id > 0) / |- L7 h h7 }. x
- { 8 t9 A) L8 ^8 z. a$ W
- --$id;
& z/ ~- {# l1 `) u - mysqli_query($conn , 'UPDATE a SET id='.$id); 2 i2 g5 l: s: Z I
- } 0 ?" e& E3 z9 n# p+ a( T
- 4 k4 [; p2 u1 v6 A
- # mysql 解锁 " {" o/ D# c, f: l
- mysqli_query($conn , 'UNLOCK TABLES');5 N4 z& n/ ]3 s: ?/ S
- //查询解锁后的id值
' K G$ X9 V! T& G9 t - // $res = mysqli_query($conn , 'SELECT id FROM ta');
' H# J) _6 G4 z9 l: Z8 z - // while($row = mysqli_fetch_assoc($res))0 i$ ^4 t. {. c- j) ?$ D- G( ~. `- l
- // {
2 w; S0 @. U" U+ u/ i+ f - // $id = $row['id'];2 v: A" g& h* s/ m( ]; V% h' x
- // }
$ h# g. U/ }' B3 Z/ _$ ` - // echo $id;
复制代码
3 u) ]; ~8 }' k& Z! v" T9 y8 ?+ }2 O$ W1 `1 w" Y2 c7 w/ {5 T
8 Q2 N1 s& F5 @$ lPHP文件锁示例: - /*: d4 e1 ?& M- F$ Q }: e/ S" H1 Q
- 模拟秒杀活动-- 商品100件
0 q, D, G$ G j! I - CREATE TABLE ta1 R& L+ T5 j' u
- (7 j. o2 V% \& H* H0 }
- id int comment '模拟100件活动商品的数量'0 `, G: h( v! w+ }, D7 @. d' o! D
- );
9 `- b5 p3 L, }( t - INSERT INTO ta VALUES(100);" H7 `, V5 h; S f \
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
! ]/ x8 s! Y8 ]$ p( z% X - */
8 m# Z& ?$ ~2 c5 f- J - 4 a- U- |% Z7 i- H& n2 P4 E
- // 关闭错误报告
1 M- [+ S- p0 k( v }' Y - error_reporting(0); 6 K% _0 t2 d5 l, y3 {
$ g, M& b' [0 z# o- $dbhost = 'localhost:3306'; // mysql服务器主机地址
1 N3 _! `$ z2 ?5 J - $dbuser = 'root'; // mysql用户名3 `# U4 g0 q) i v2 U+ ~
- $dbpass = 'root'; // mysql用户名密码
7 ?: ~# Y) B# d3 T8 n' u - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);( Y# ^6 J) |9 p3 p3 |
- if(! $conn )
* L7 x" N& h, g* B9 A. U4 o* @3 l) g - {& W: x4 _. v, Z8 S2 n! a, `
- die('连接失败: ' . mysqli_error($conn));. v P8 J- ~; [3 [/ X" D6 b
- }: g9 v7 j- q1 o% S8 B
- // 设置编码,防止中文乱码# ~4 V/ p+ T7 B I
- mysqli_query($conn , "set names utf8");+ Y/ u+ h) x" Y' g" r
- mysqli_select_db( $conn, 'temp' ); ) u E) r# ?( r! T3 L4 C
( r, O! P/ k, r* H- # php中的文件锁
/ v# f8 I. T& O7 M- {9 G - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 7 D( h* J% a8 k- D3 Z$ u' e! J
- flock($fp, LOCK_EX);// 排他锁 * I$ ?5 Y) l7 M- A
1 Y$ x f8 e, _: H0 f* ^8 N! S$ I- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 0 ^) i& ^( c/ d6 Q4 C! c: v
- while($row = mysqli_fetch_assoc($retval))# B+ w9 Z& O1 K1 [
- {% x2 r: ?: ^4 ]' t3 n% c
- $id = $row['id'];
! I1 R2 \( o5 q- L! | - }8 H; U3 k. h/ w5 U% Z. \
-
! L# J/ o" G' P- Q- x( h - if($id > 0)
& N2 u1 B2 R' f) N - { 9 A' B3 B7 s0 Z8 V3 p J$ A& U
- --$id; 9 H- T9 O$ C0 }3 v% h$ H9 Z: ~
- mysqli_query($conn ,'UPDATE ta SET id='.$id); 1 {% Y- h% m/ V' \. K
- }
9 }4 M" S' _1 Z; [. _* D% _% O) W - # php的文件锁,释放锁
3 g5 R- I5 w0 U6 C# h. e- Z - flock($fp, LOCK_UN); : N$ m$ {4 |& {" S
- fclose($fp);- u% C* Q. E, s Y- D
- $ F/ f ~9 m+ t1 J$ o9 a3 S. Y
- // $res = mysqli_query($conn , 'SELECT id FROM ta');, m6 K: J+ y. N/ a7 v9 t0 i) m. a/ X
- // while($row = mysqli_fetch_assoc($res)); A8 ]9 `& O, L" p0 }5 n
- // {: u; s# U( R9 U: L7 ^" x4 }! o
- // $id = $row['id'];+ `( G8 e4 W2 a' N- m
- // }
* m; z* q1 c0 _1 Z4 ~ - // echo $id;
复制代码
) t$ X& U. i2 X4 ~' K6 I
3 i- |. K; o) Q6 \4 v抢券活动实例: - public function envelopeSnatching(){& X/ H4 X& t# F+ Y: @3 r& m) r: n% c
- $lingqu = $_POST['type'];
. d- f8 B8 h/ j - $uid=session('u_id');//用户id
) l; h; R4 O7 A2 X3 \, k: P% ? - if(!$uid){
5 ^# T) d6 e6 F* \# H0 \ - $data['msg']='您没登录,请先登录!';, L% T+ _! L _1 _/ `3 [
- }else if(date('Y-m-d') != '2017-12-12'){8 l, i6 X5 b6 \# E
- $data['msg']='不在活动时间内!';
. Y C" B+ f N: L3 ~; K! E - }else{
- @' c+ H3 u4 T - $hours=date('H');//当前小时数
# |4 M2 W9 Q# b- n) V, E5 d - if($hours > '09' || $hours > '17'){
. v9 p0 V" j* Z4 J6 i& G - # _- L! }, z1 F
- if($lingqu == 1 || $lingqu ==2){//点击10点的
/ m7 l+ B6 d$ y& @+ {: M - if($lingqu == 1){5 Q; H' [7 y' \1 g4 I4 s" I. N
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
) k8 }9 j' z5 R( x2 {) b1 s - if($hours > '09'){8 W! U3 s P! G9 E. o/ c
- $num=mt_rand(25,28);//优惠券金额
! x& u! j/ K k% ~ - $id=1;
/ U, A7 Y4 x+ d# _( i - }. B! m8 p3 l$ C- }3 u( V* r1 M
- }else if($lingqu == 2){, A7 b' v3 p- S( b! ~+ T
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();8 G m1 Y2 [& m5 |6 c
- if($hours > '17'){9 O# ^- Z$ K: x! s( |
- $num=mt_rand(50,55);//优惠券金额& Q! K" a; m! J$ W! R
- $id=2;
" v/ X8 Y. P1 O8 z6 h; ^0 A0 S R* R - }
: n) \. r# G' Q. A* \ - }# u3 ?- ^. m( Z1 e" z1 ]
- if(!$id){" }" S Y$ U+ [; ^9 R
- $data['msg']='时间还没到,晚点再来吧。';
! l0 A: V# P) @; K0 Y1 Z - }else{' }" y# I/ B" Q7 o) [ r6 g
- if($is_lingqu){
2 Z1 q' l* c( x/ x4 e - $data['msg']='你已经领取过了,留点给别人吧!';
j2 U% I, v) u - }else{
: B$ Q; o/ A P; K! e/ J8 ] - //锁表
" k. Y7 Q; h1 I, g% v - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');" ?1 c; S* X) u4 h7 s9 H3 R8 c
- $active=M('active_num')->where(array('id'=>$id))->find();, r) S' Y+ X: t( r/ c2 d5 h+ }6 Z
- if($active > 0){* u9 b4 N; |* u
- //开启事务
) D$ m) }+ p* R! E2 v - M()->execute('start transaction');* s. _- ~1 Z* [3 F
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
3 J7 m; I; ^2 d9 }* I7 c; T; a - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
T- Q, X7 B! g% Y2 h' p3 A) t - $members_preferential = M('members_preferential');
. G. |7 Q' v$ G# w2 [- m( F2 x - //对应投资金额,# `/ z* ?- n0 o. V- d d9 l
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));6 e6 T9 I F, U4 p4 t
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
$ o# q& g4 W f5 A0 q - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间4 z m$ W+ a8 l
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券- K, @2 R6 ?- K- z& d' I r8 q& |2 O
- if($save && $add && $add2){
% ]3 h/ b! W: Y/ K% \# O - //事务提交7 T5 M- [9 \8 [& n* n% n3 V0 l/ e! i
- M()->execute('commit');8 m7 Z2 p6 B. A: @9 J- z
- $data['msg']='恭喜你领取了'.$num.'元优惠券';+ x. ]5 C: p& F+ o3 y: L4 P% C m
- }else{
1 i& K$ U* h) D U - //回滚
7 w: n% Q7 P) }. Z& |) i7 N - M()->execute('rollback');
. ?* G0 g" t: x) U, N - $data['msg']='未知错误!';
2 d4 C' `- @0 e! g4 R q1 b - }
- S L+ U+ \- }4 r4 D# t! n6 U - }else{
?$ h* Z% H: F. C6 W* m - $data['msg']='红包已领完,你来晚了!';# ]) W# I* E1 T+ K1 g9 C3 _+ n
- }7 [2 o; A' Q7 Z: a+ M9 F6 |6 o
- M()->execute('UNLOCK TABLES');
n9 j! Z1 s, x) y - }7 k: R4 u( \ P$ Z
- }
h6 C; r T& T+ c- |# B - }else{! E) h6 i, p, s/ v
- $data['msg']='非法操作!';
! ^, v* E' f& h8 i( } - }7 v# K. O) g3 R
- }else{
6 b' G) I- B/ D8 M( b - $data['msg']='还没有到活动时间,请晚点再来哟!!';
) Z l$ Y& }' {; I. s) t8 E% } - }# F+ _/ L0 ^" S
- }
8 {: r& V/ y1 C8 t* i - exit(json_encode($data));
% y3 k z, G1 U% t2 u; q- f, k - }
复制代码
* c$ J. N2 O" F( ]
2 H5 D" e% [/ h" i% P |