|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
6 N/ t# e4 p$ F' b+ r2 HMysql中的锁语法:# W- O* n% y% d# F/ S' U# p
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】; n: ^3 Q( A6 t/ f/ S9 h
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
* t! U2 U6 v8 j6 E1 q$ l3 H) }3 uWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞' S! m( T. A L
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :& u! r" f% m( W* i2 h6 L' ~5 V2 }
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。8 @* [9 ~% K! M+ o7 J8 s
测试时,有个文件就行,叫什么名无所谓 总结: u# E1 @6 l* ^3 S& b
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:! a) ]5 q3 l3 p* ~7 ?; a7 L! q
1. 高并发下单时,减库存量时要加锁
$ H/ T2 x9 ^' l0 F8 S2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
0 K, L; g: q9 e9 U: L3 B - 模拟秒杀活动-- 商品100件: {7 w' C5 J3 E( t
- CREATE TABLE ta" A+ Y2 d2 _7 G; x
- (! Q5 C& r' i# e' P, ]7 k% @/ h
- id int comment '模拟100件活动商品的数量'; L- c! `2 o$ t! v- P
- ); f/ w7 V; Q0 G7 |) P. P" y
- INSERT INTO ta VALUES(100);9 U" p5 J$ J d5 ~2 H# u1 q
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
/ z+ c5 s5 k6 @: Q I - */ ; [! f! x. \# f$ S5 e! r1 f2 J
- & d" B6 g$ M" f. `: P" ]
- // 关闭错误报告7 ^: v# y! U- q: G. N5 O
- error_reporting(0); # }0 |9 u( v& a; u9 N1 C
- . G" R& `& j9 q; d/ K: ?/ u
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
+ B( ]2 I% W! ?7 c- P - $dbuser = 'root'; // mysql用户名
4 V P0 ]2 H2 g - $dbpass = 'root'; // mysql用户名密码
6 R& s+ w; f# ]" h - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
; ~7 p* B5 _' Q( X/ L9 v3 s$ @ - if(! $conn )
1 H! _2 c( K0 b; I& T - {
0 d6 ]% |' c* I1 M4 W0 m2 } - die('连接失败: ' . mysqli_error($conn));* C, r% m4 H9 Y! g5 W/ W/ R
- }
0 l7 G; S4 ~2 j/ \9 A9 o7 V - // 设置编码,防止中文乱码
3 I, q7 Z3 q, R1 P - mysqli_query($conn , "set names utf8");
% ~; h* \4 |! K% Q - mysqli_select_db( $conn, 'temp' );
/ C, x' M% }% n& ^0 K! x7 {/ [. u
, C' Y6 N. V) L+ e0 t% R6 l9 L- # mysql 锁 . b# g+ l0 ]$ ^$ u
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
& A0 \; v1 R6 p9 D1 s - $rs = mysqli_query($conn , 'SELECT id FROM a'); % N' |- C( F' f c' a
- $id = mysqli_result($rs, 0, 0); . X3 }( a0 i6 X5 J9 u$ J9 W9 c
- if($id > 0)
) f1 ]7 G& e1 K2 n. P( } - {
! x2 q- k2 S. C& t2 J- e/ v - --$id; . }& Y1 }: B% z; C
- mysqli_query($conn , 'UPDATE a SET id='.$id);
$ k0 x B* S" h4 n! t4 w7 V - }
+ C% N9 @* U1 G+ r - : l9 c" U& D: a
- # mysql 解锁 , n$ H3 o- S. J1 w4 r; g
- mysqli_query($conn , 'UNLOCK TABLES');. Y: W" O! ~$ ~* V, G0 q8 z
- //查询解锁后的id值9 z2 v9 I: {- n5 E/ f
- // $res = mysqli_query($conn , 'SELECT id FROM ta');4 Q5 P: V' Z7 F' q% _5 }3 Z! E
- // while($row = mysqli_fetch_assoc($res))2 ?4 F- Y$ f3 a
- // {; n' J$ y, _1 z, T
- // $id = $row['id'];
+ G7 }7 N1 Q, F7 u - // }0 I4 ^' v9 Q& ^4 p
- // echo $id;
复制代码
5 c4 J6 Q6 W" m# |
: o! O, G1 |* ]0 I
1 N- T1 W, H {5 L5 e" VPHP文件锁示例: - /*; e# ?& \; Q$ K4 }- F# ]
- 模拟秒杀活动-- 商品100件
+ N0 x. T6 P5 [9 s2 E3 g, | - CREATE TABLE ta% {% Q0 `" O% ?0 t5 ~
- (
# ]$ i! T6 u% q* H$ C- G - id int comment '模拟100件活动商品的数量'. ?! Z! X, c6 z/ o1 U* X O, z
- );
6 `1 q' y+ |4 @0 K8 X - INSERT INTO ta VALUES(100);
0 T: d2 G" e6 C. K. Q - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件4 C( w, T6 Q' q/ a
- */
8 K' ^7 a" M( Z( a. m" ` -
* ?$ n# m" r) O2 I- _ - // 关闭错误报告2 m2 r6 n9 C4 F/ s0 K' L
- error_reporting(0);
: o* j0 B, v; b2 {8 ?
4 ]9 Q" N! N% U$ Q- $dbhost = 'localhost:3306'; // mysql服务器主机地址
! M" h/ K- W5 Q. C& k, z9 ^% Z! z - $dbuser = 'root'; // mysql用户名) U3 ~# T5 b# l" ^; Q
- $dbpass = 'root'; // mysql用户名密码
+ T5 |: w M- _% D - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
+ H# w$ J& v- H; z( I9 ?! o - if(! $conn )
7 ]+ h7 s0 B. b - {) a% `1 f( i1 ]4 {' ]
- die('连接失败: ' . mysqli_error($conn));6 d) F; T) V7 S/ [7 `8 k
- }' J# p$ C* B& U5 f4 Y
- // 设置编码,防止中文乱码9 h' R$ [1 E, U, V( q$ h F S
- mysqli_query($conn , "set names utf8");
2 T& l6 D+ F7 [2 i% h* a( e1 H4 E - mysqli_select_db( $conn, 'temp' ); 8 i$ c( U' Z) s2 b" W; ^3 P
- + _7 E4 Z X: ?5 s
- # php中的文件锁 2 Z+ s6 X* X+ z, N1 T8 a
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
3 e" y) e9 `+ @) e+ T! o$ ~- K - flock($fp, LOCK_EX);// 排他锁
/ a4 J, J- D2 u - ; O8 p2 r: E" R' ]. c
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
: S: ]1 o; N1 R& H# Y' r4 x - while($row = mysqli_fetch_assoc($retval))
. W8 E! @& b0 c5 D+ j# `: @4 H - {
& n0 f, ?& g: m - $id = $row['id']; F9 t( b. z2 i& [* q( X9 L
- }
8 Q* ]' N' B2 K( |, @( {3 {/ x( q" P# ` - - S6 v. v# T, n* P: V
- if($id > 0)
1 l& W3 Z+ a T/ ]: s - { ; \! g# Z/ r8 p
- --$id;
6 w g0 g4 v B: N- s - mysqli_query($conn ,'UPDATE ta SET id='.$id); & T! m3 V4 d# j1 N1 r6 L- s
- }
# q9 k5 S9 R& r* c0 h5 U4 a - # php的文件锁,释放锁 . F9 X) D6 O1 y, c. B6 N- I$ s
- flock($fp, LOCK_UN);
# G Q9 @1 t4 d y& I) G* { - fclose($fp);% c- O o# m0 `
$ i+ C7 e5 g4 i& S. M0 h- // $res = mysqli_query($conn , 'SELECT id FROM ta');1 V! [0 ^2 M% v/ l1 ?
- // while($row = mysqli_fetch_assoc($res))3 _ ]2 e: w9 K0 S6 w/ ]$ ^8 V. `" a
- // {
- t; S! v; Q; i3 I6 n+ |& ] - // $id = $row['id'];- E; N) Y2 w$ |: A6 f
- // } P* I# {3 t: n2 f7 r
- // echo $id;
复制代码
^4 H7 p0 R8 f7 s0 q4 ?6 n1 D- e4 C. t4 k6 }
抢券活动实例: - public function envelopeSnatching(){6 h S9 r' j) w/ m4 e9 B7 c
- $lingqu = $_POST['type'];& L) v- Z, N" j9 C9 m9 m
- $uid=session('u_id');//用户id
k; H" q9 V$ @4 A6 |7 e0 D2 g* H - if(!$uid){
; I! C: L/ Z- P2 a - $data['msg']='您没登录,请先登录!';
0 U# J/ l3 p: O! [' R! h9 I2 z1 Z; Q; Q4 Y - }else if(date('Y-m-d') != '2017-12-12'){# ]' N. M3 ^! F1 E! }* g0 i
- $data['msg']='不在活动时间内!';6 i- Q0 m2 d% a% M
- }else{; A# ^. x( ~9 a& [ i1 L
- $hours=date('H');//当前小时数
5 v# d9 [0 V! c" B$ g! j" O - if($hours > '09' || $hours > '17'){$ r$ }' v7 K/ @8 h& N. \$ R2 k3 k- M
" a( \8 y' z9 a. M- if($lingqu == 1 || $lingqu ==2){//点击10点的
2 {7 G6 W+ Z" _9 J0 H$ s8 b/ f - if($lingqu == 1){
& x$ }9 E* G z: C9 a. L* ^6 ^ - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
w* \1 B& N& M) M" g - if($hours > '09'){
2 ~- e5 ?3 ]5 }1 b! y- ]9 S - $num=mt_rand(25,28);//优惠券金额* e; z1 e5 N' Z# B) R8 o, j+ f
- $id=1;
+ ]& u' o% ]1 v: t2 H% I' J - }
' R" H: U! E( ]' c1 R - }else if($lingqu == 2){
5 @: R! R" A4 X5 h( @ - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();4 h6 z2 M' h3 F& f
- if($hours > '17'){
' K* C y4 w" X7 T - $num=mt_rand(50,55);//优惠券金额
3 t- v% \" N, s" k1 I& } - $id=2;
9 l" z. L/ H" d$ C: m - }7 L. e; }: q) d4 o0 v
- }2 Q Q c$ ? \2 P4 G8 @ ?
- if(!$id){: o+ R" V2 F+ g2 P2 b
- $data['msg']='时间还没到,晚点再来吧。';" W! W5 V. }) [( w
- }else{
' Z Q3 @, L$ K0 s' O - if($is_lingqu){
R" \# @" Y1 c/ u - $data['msg']='你已经领取过了,留点给别人吧!';) T( Z2 p2 O/ ~$ ^. X% h
- }else{/ j$ l: A3 @9 P$ _3 `
- //锁表/ q" e7 w& x$ o9 H" O
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
* [; k( |3 m) q+ ~ - $active=M('active_num')->where(array('id'=>$id))->find();# ^: f$ W- t2 |- N/ B v
- if($active > 0){
7 `6 E( [5 [ c& R' f/ Q, ~ - //开启事务
, }2 K+ L* P9 k; H( F; D - M()->execute('start transaction');8 d! n& s5 C0 R! T' M6 w
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));6 [& a6 I3 S# ~
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));/ K8 a e% s# [7 H; x) d+ j, M* U
- $members_preferential = M('members_preferential');
% V9 u+ d" t' x6 t0 O2 ` - //对应投资金额,$ Y; S, ~- d& E8 l+ @0 e( x! _4 p% ^5 `
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
i5 n; N8 H4 I# G1 V8 @$ h. F - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
1 ]' N, \/ t8 _/ P3 Y7 v - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间. v6 J3 Q' L+ P# I/ k
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
$ k) P5 }$ w& v - if($save && $add && $add2){9 H3 I! K% m' ^& c0 e# f! e+ L; L- N
- //事务提交
L1 p% p4 `* \ - M()->execute('commit');# e/ l, E1 b( ?$ I, R- l2 K+ _! }
- $data['msg']='恭喜你领取了'.$num.'元优惠券';2 j8 n+ D/ H% @# C. }
- }else{4 x( s( p+ a# n( F
- //回滚% M0 p' n: r! `! L- n
- M()->execute('rollback');
9 n& g8 a& W" R- ]2 ^1 u) M - $data['msg']='未知错误!';: m- w+ X! i& ]$ J
- }
+ o$ {/ F& V, X$ O - }else{
& f8 \+ S G) r5 t - $data['msg']='红包已领完,你来晚了!';
% r* Y$ b5 {8 p - }
" R, Q$ N6 Y, r, {# W1 i - M()->execute('UNLOCK TABLES');, S* b- @. T7 N5 p! A3 {4 `
- }
0 c7 m) E4 E; e! v; W+ j8 N- n - }3 \6 {/ O2 X) n, h6 V- ]: c3 w9 _7 N
- }else{8 H& f+ ?# i) B% ]+ h; i3 }: w
- $data['msg']='非法操作!';* @$ r, g7 f" ~- k( j: F
- }
- n% K; r* l0 X- V. C- u, [9 b, n4 } - }else{
# ?4 q' F/ E$ N1 l - $data['msg']='还没有到活动时间,请晚点再来哟!!';
: k& v3 g1 N, e" P! p - }
5 _* o7 `, ]( }. @ - }6 U- I2 M4 r7 Q; ^6 W& X6 H
- exit(json_encode($data));
( H0 F% u- J- y) Y; C. @ j+ v& A - }
复制代码 3 g- G: U @ B+ C
4 C" |' i n% b, O5 v4 `$ [5 w
|