|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
# q" \' u! Q2 P# S2 S: SMysql中的锁语法:
2 l9 O9 j, O, l+ L! n) W) lLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】1 r' p$ g! P7 h, @4 h
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表/ l! X8 y( E8 R' X" O
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
' q6 S, F1 p" X, a注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
6 k8 y3 L9 J' _# a文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。8 k3 f/ ]# e2 i2 [
测试时,有个文件就行,叫什么名无所谓 总结:& I! A4 M+ t7 D
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:& d) K' v2 j y- I1 r& o5 H
1. 高并发下单时,减库存量时要加锁
# k* T0 d5 q) T/ L6 [4 Q2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
c' p% r, ?) v) l a! O2 r8 }4 v9 z - 模拟秒杀活动-- 商品100件, h8 b* |- [/ ]& v1 W
- CREATE TABLE ta% h: x2 m" Y& n) c% a
- (
' J1 Y! U1 D$ ^4 B: r: {; A' u - id int comment '模拟100件活动商品的数量'( `1 f8 D- X- R, S4 A: i1 N
- );8 c) P4 V3 y7 x8 a' x M! G
- INSERT INTO ta VALUES(100);
/ S! R' Q! x \ - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件: z G+ M# U0 P; M, i, I
- */
% l7 u' R3 P. m C0 m( @; S& ~+ \8 d - $ ] w$ H! V( f3 X U; g
- // 关闭错误报告
* K4 Z3 n6 F* c E - error_reporting(0); . b, e, V# `8 v" W
- " W" D$ r6 [/ ?, W% }
- $dbhost = 'localhost:3306'; // mysql服务器主机地址$ v8 m1 K1 @2 u! ]
- $dbuser = 'root'; // mysql用户名2 R& U1 X) W6 a" I6 I8 X
- $dbpass = 'root'; // mysql用户名密码
( k& z0 j+ U- _ J+ `$ D& T - $conn = mysqli_connect($dbhost, $dbuser, $dbpass); p8 C* s7 g. S9 z9 g
- if(! $conn )
* i0 S; n! G7 z! f# H; i2 P0 w2 G - {
- s4 _9 j7 q5 {( B# q - die('连接失败: ' . mysqli_error($conn));
; F+ ]. ?, Q5 D0 D0 G - }1 u k" ?% n0 ^% j. f! G" l! D" [
- // 设置编码,防止中文乱码
3 T ^( R4 _/ ]# ^: Y; U - mysqli_query($conn , "set names utf8");
( k7 ^& o4 A2 p1 S7 N0 W+ D - mysqli_select_db( $conn, 'temp' );
, X \7 d6 |" ?& |" p8 x' O
3 K/ o9 |; P* r2 t- # mysql 锁
# @9 S" r9 \& s( M b% [% L9 x% g) D0 P5 W - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 * R, E) f. k; h; s
- $rs = mysqli_query($conn , 'SELECT id FROM a'); 2 [- T; ?( q' @& W) k, M
- $id = mysqli_result($rs, 0, 0);
5 w) I7 `9 H" l) |) ?3 L - if($id > 0)
4 b1 v% J' ?1 P# o* D; A7 C: T - { 5 }' O X) ?8 x7 p1 |
- --$id; 0 w" {8 l) q3 N. ^6 M* O
- mysqli_query($conn , 'UPDATE a SET id='.$id);
( {1 O! E% m* j) l% N* G! G5 b - }
8 Z' N3 E0 g. u
$ s& b T8 _! p% J4 T9 p0 D2 z, u- # mysql 解锁
, J& B; g# i o# f& u8 ~+ t - mysqli_query($conn , 'UNLOCK TABLES');) _4 t2 B4 i9 p( R& x# e$ l% V
- //查询解锁后的id值
* E9 g8 Y8 _* ~6 B5 ^ - // $res = mysqli_query($conn , 'SELECT id FROM ta');5 l. h3 t9 f0 J* ~( p. z
- // while($row = mysqli_fetch_assoc($res))8 H( s, T+ A- I2 @, n
- // {
( R/ \, f+ {5 b7 \4 w; Q - // $id = $row['id'];" G% Y7 X, Q; [3 \
- // }
& X& S: I* ]" k1 [$ l; ]) }2 ? - // echo $id;
复制代码
; t! ]7 [2 O4 i/ E: E# C2 x
9 D, a) \4 {' V- `: Z7 H3 O! J) O* D( H7 ^ ?7 S* `
PHP文件锁示例: - /*
# R! Q* V- t" u: w I - 模拟秒杀活动-- 商品100件) l3 N- T: I- a: I
- CREATE TABLE ta6 _3 y) Q* X% l0 N+ D. R3 R
- (7 [- J6 A/ d }9 \( n
- id int comment '模拟100件活动商品的数量'4 @4 w2 Y2 D' I2 j2 N) |( j
- );! P% J1 o8 u- y y3 U& x, j/ y; P
- INSERT INTO ta VALUES(100);
- y& y: X- u+ @' F, Z! }$ H7 B; U - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件% j, ^) r7 j1 q% _- A) D, j% G
- */ ' F3 e2 N& r+ g0 O
- ; h2 H0 U# C- }2 V+ ]3 z
- // 关闭错误报告- v- ` W$ @8 W% O2 P( [1 Q6 b
- error_reporting(0); 2 w6 X8 @4 b4 t. i5 q& o ^
0 F( q2 X N1 U$ E7 j/ a$ G5 g0 y5 `- $dbhost = 'localhost:3306'; // mysql服务器主机地址
5 F# m3 K$ ~& Y - $dbuser = 'root'; // mysql用户名
& [2 W$ ~9 k+ A! `$ f7 w - $dbpass = 'root'; // mysql用户名密码5 N2 I. l: @! M6 M2 v( b- d# p2 t
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);6 y. }1 H( T* a7 {1 O% q a
- if(! $conn )
0 ]" `/ u; o8 g5 W1 ?- m( h4 S - {6 U: o% ]. w# F' t3 z- `3 e
- die('连接失败: ' . mysqli_error($conn));
' X- Y1 q( @. u8 I/ v u5 a6 Q v - }
8 n( O* M( H' q2 P- G0 q - // 设置编码,防止中文乱码2 x- S- F+ }( _: e6 ^7 K0 H
- mysqli_query($conn , "set names utf8");* I( L8 E* s2 m: _: _# ^! D
- mysqli_select_db( $conn, 'temp' ); 4 ~% L0 R& q7 a& L/ U
3 y$ J1 k/ }, W- ?) n- # php中的文件锁 - w; z" C; L8 g9 K8 q% v
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
# ?; K% ^ b( _& y( G - flock($fp, LOCK_EX);// 排他锁
- s' z% |4 U/ d3 e9 [6 x
% _3 Z7 i, K* X! ~- $retval = mysqli_query($conn ,'SELECT id FROM ta');
5 P! w& q1 ^1 ~( t0 y: o' x - while($row = mysqli_fetch_assoc($retval))
7 Z7 a' }; q- H; e, ~/ f, N4 f7 w - {
# O' d3 D/ N; y1 D. |& T4 G - $id = $row['id'];& K# _7 `( E. Z" V+ L5 e5 [
- }
2 E g1 E2 p' Z% Y t7 H - . W$ ^( f) I1 h. U0 R7 [1 K+ f" d
- if($id > 0) 3 z' N; F7 I' {5 U$ V8 F
- { 5 K$ i8 L8 }* n* d4 o8 H, m0 d `
- --$id;
/ E4 [2 H7 U1 x, v* z/ X - mysqli_query($conn ,'UPDATE ta SET id='.$id); , P9 v# e6 c) l1 }) J
- } * v% u% y: f- y5 w* x
- # php的文件锁,释放锁
* P0 l. H. K2 b% K* l4 Y, K* L - flock($fp, LOCK_UN);
% v0 p2 x/ | B1 H- Y( o8 O- { - fclose($fp);
/ i8 t. h7 i4 {: p; g
) {7 x9 v$ o$ {/ S, a, Q- // $res = mysqli_query($conn , 'SELECT id FROM ta');
' B7 j1 g H9 S - // while($row = mysqli_fetch_assoc($res))
# ?+ Z6 X) K" l; S" ` - // {, S; w0 d+ C( b
- // $id = $row['id'];
6 {5 X0 e& R& `; ?% o - // }" U6 M! o' C% q7 m5 _: Z( Z
- // echo $id;
复制代码 $ x3 Q% Y8 ]" l, O
$ s. W5 o2 o( S
抢券活动实例: - public function envelopeSnatching(){, F: Q* h+ v- Z: l& @$ Z
- $lingqu = $_POST['type'];
3 |/ `' d# q+ C! M - $uid=session('u_id');//用户id
U6 |! h: G! }( ]3 G$ d" U: z - if(!$uid){
1 A4 K# Q1 I' u4 j! x) q+ G9 @ - $data['msg']='您没登录,请先登录!';) U, ?! s$ \6 i* |9 \2 l4 E; D
- }else if(date('Y-m-d') != '2017-12-12'){; {0 e3 W+ Y& o
- $data['msg']='不在活动时间内!';4 e# ~6 J0 \2 Y$ x4 x4 z
- }else{
: f8 E4 p- r0 l- S - $hours=date('H');//当前小时数5 ~" ?9 U) \: @1 J K$ g* n# p: Z
- if($hours > '09' || $hours > '17'){; a$ a# s3 D2 y; e
- 7 G/ |6 q7 x1 o4 U7 ^7 @, `
- if($lingqu == 1 || $lingqu ==2){//点击10点的" R1 O3 z6 ?* J( z5 W
- if($lingqu == 1){5 Y0 H9 U% ~3 N. @, K7 D0 H* D
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
. S* t w" T- u! g/ o2 w - if($hours > '09'){
. H8 m. F% d: F6 f - $num=mt_rand(25,28);//优惠券金额1 ]7 x. \, B4 s
- $id=1;5 N- v3 ^$ B! \) r2 g+ a
- }4 H* V* \' K! H/ b* R
- }else if($lingqu == 2){
: h1 A! f8 F7 e; T3 R3 i6 f - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
! ^3 O$ y: A, E$ d" Z+ b" | - if($hours > '17'){
) M& |) O! n/ E - $num=mt_rand(50,55);//优惠券金额
/ n0 U" y# a7 @* T3 N* b& z% n - $id=2;
& f# u2 L% Q4 ?' r/ v4 T6 J; S - }% i P# L# E$ Y/ r6 C
- }
& o/ _: [3 [3 _. u5 H) r7 H - if(!$id){
2 d+ p) O6 u0 [1 F$ P/ Y1 J - $data['msg']='时间还没到,晚点再来吧。';* ?8 Y% I5 M0 E; G9 ~2 @( D9 m
- }else{' T" G% Y3 H0 h
- if($is_lingqu){, N$ M; t$ @1 q. c$ u! W+ ]; a6 A
- $data['msg']='你已经领取过了,留点给别人吧!';
' u1 g% o6 ^3 n) d% e2 w+ e - }else{
( g6 N& x- m" A" C! Z - //锁表9 z3 E8 d: |% J0 q1 P# B
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
4 y0 N/ A4 O- R! k, r* I# n9 y - $active=M('active_num')->where(array('id'=>$id))->find();' B I$ @- x9 g0 M6 ]
- if($active > 0){; p! v$ K& c, q, t
- //开启事务
4 E4 S5 p2 B* |. ` - M()->execute('start transaction');
* H# D8 c! ?/ |8 @+ y9 h - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));& E- V; v' t4 ], r1 @
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
8 j, F! r w8 _' K4 S1 J - $members_preferential = M('members_preferential');, e5 N3 Y+ s% q% ?( g U
- //对应投资金额,
' s8 H. w9 |" I( Q) b8 i2 L5 @ - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
5 x* B/ [+ p! [# O3 s - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');( v- ]: x) p. ~! z% I$ T3 ^. U8 D' i5 k1 C
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间6 K7 T ]/ x- A0 ~( Y* B0 W9 F5 U$ ]. W
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
: g, ~" E5 D$ F8 c# u+ x% g - if($save && $add && $add2){
& ]) P* N8 Y' O - //事务提交
& [* V& A6 K( X; ^% q1 D' S - M()->execute('commit');8 P7 N1 z% t- I) G
- $data['msg']='恭喜你领取了'.$num.'元优惠券';9 e6 M1 ?, J9 j# I4 ^8 c
- }else{# z4 g6 r( S( a- |' } h9 I: }
- //回滚2 I! E0 m9 V6 A6 W& h# l
- M()->execute('rollback');
5 J: E2 h; _' a0 B - $data['msg']='未知错误!';
7 t) d+ t9 w4 |0 M' W9 Z* K - }0 _7 E! Q; S2 o5 M: ?. c2 q0 k9 S
- }else{
" L$ t* a) @! X) Z6 M: q* f: d - $data['msg']='红包已领完,你来晚了!';
0 k$ A8 v) Y$ c2 a - }, ]: y) N1 i T* P
- M()->execute('UNLOCK TABLES');
0 o. @; x' J/ L2 g - }4 s8 T) L; `7 D
- }, F7 m1 E- G! J8 n/ f) C
- }else{! M, n2 F+ b! N
- $data['msg']='非法操作!';
! `9 L6 R1 U9 E7 c0 N2 \; P9 `7 s - }
" j, c' m- I7 ^ - }else{
0 I+ \9 {- \* g& W+ e( l& c - $data['msg']='还没有到活动时间,请晚点再来哟!!';
' J5 ~3 b6 X' Q3 h) n - }/ a3 ^/ U8 u) m1 \
- }7 q3 v' R _1 y$ Y" y8 j+ ^6 b
- exit(json_encode($data));. m2 f- L* ^5 A) s4 i/ o3 E* |3 N) i& Q
- }
复制代码 - V6 e+ z, F0 [( J- v
1 l/ x. O( O1 E; P. {
|