模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
! c$ Z: F4 @% M$ E* }5 G- TMysql中的锁语法:
! q" b7 m8 O% r2 M4 }$ bLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】2 x1 w5 `' X4 L
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表" ]# m) j& p/ O7 W
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
2 P: {7 Y( a1 v" {8 ~8 g! C/ | }注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :; }6 n1 L/ F% R0 q6 G
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
' [( R. S. x; P/ K测试时,有个文件就行,叫什么名无所谓 总结:
3 M" v8 x/ k. |& P2 v5 t5 A项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:9 A* X$ M8 o9 c* o& S( r5 S
1. 高并发下单时,减库存量时要加锁
) N: V+ ]& ?5 n- O' a7 `7 L9 O6 N: m0 L2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
! ?3 J6 \3 ?% J9 @% j$ R7 t' K - 模拟秒杀活动-- 商品100件 k# [& u+ J/ d; P$ a
- CREATE TABLE ta
/ p. T1 M) R @8 ~: t7 c7 m: i- N7 B* G - (
- m, |2 q* A$ R! b/ m' a1 k& P - id int comment '模拟100件活动商品的数量'
% u+ _( U0 T9 Q' k4 I( d - ); E, `1 J4 G3 B
- INSERT INTO ta VALUES(100);2 \7 v' f. }9 X0 ]% ]/ ?
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件' F. N( e; F) t5 E
- */
/ t# g" b, @9 G -
, {- o6 Y3 H% _9 I% q) A. C2 B6 r - // 关闭错误报告' V0 I/ ]# k3 ]. h6 i4 S
- error_reporting(0); 0 b" N' J1 Z o0 a( x3 Q
+ d1 ], J5 o0 x$ U5 `- $dbhost = 'localhost:3306'; // mysql服务器主机地址
# _1 N8 l3 Y$ I/ K7 A0 U3 m) P - $dbuser = 'root'; // mysql用户名* h2 t- c# {- f5 R0 v4 ?* M/ D
- $dbpass = 'root'; // mysql用户名密码) g. Q, \5 V; W0 v
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
/ M6 q& w/ d- I7 C7 F: `' l - if(! $conn )4 y& O C N, R n/ j
- {
: J g% x( c6 M V+ {' |: L2 a - die('连接失败: ' . mysqli_error($conn));) |# A `9 x3 w# ^3 ~6 S
- }6 v7 p0 V2 j3 E' @3 u' W
- // 设置编码,防止中文乱码
5 l0 O/ l$ r, ~* p - mysqli_query($conn , "set names utf8");' Y; p, w7 v$ Z. m
- mysqli_select_db( $conn, 'temp' );
; o' ? D7 a# q( H; I
. R6 G, [/ u. X! q- # mysql 锁 3 v; e/ R* `3 F7 I2 V, {. \
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
$ w2 h5 A4 M) Z: D - $rs = mysqli_query($conn , 'SELECT id FROM a'); ) i, ^8 B) X- y3 @
- $id = mysqli_result($rs, 0, 0); 5 e; h# p9 F1 j
- if($id > 0)
6 _% ?- S+ \2 X6 V) _ - { ; P$ |+ q* `' ^
- --$id; 5 I/ D6 M) i: V, E6 \4 C5 w
- mysqli_query($conn , 'UPDATE a SET id='.$id);
' ?7 u1 {& X+ d! ^3 x - } " y5 I; E7 H. g
0 H# K, G: j3 U* k- # mysql 解锁
; ~) ]: D5 g* ?3 w d& D5 B& O$ o - mysqli_query($conn , 'UNLOCK TABLES');& C" w! s3 A: z6 T: t
- //查询解锁后的id值* ^* o3 T* v7 {
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
" H6 M4 [3 c7 _% N* d# w. ? - // while($row = mysqli_fetch_assoc($res))
T2 p+ J& E7 [$ }" }; ?# i - // {
; q* ]3 f/ J( Y; x9 S0 S$ B - // $id = $row['id'];
$ ]: j- m) j- ] - // }- D6 H! c' o7 M z; ^& X
- // echo $id;
复制代码 0 k1 W6 @6 g) a b2 ~
) P6 s3 C; Z' q. z
5 k+ b$ g. Z% YPHP文件锁示例: - /*' T# ]8 G+ S3 }5 Z. V& J
- 模拟秒杀活动-- 商品100件% u3 d4 @; z! N6 M4 }/ C4 p
- CREATE TABLE ta
, ^ h- D, V' Q# D9 j - (
6 h/ ]7 T, t$ [+ n - id int comment '模拟100件活动商品的数量'
' v1 L, }! ^, h3 G0 L I - );" l; m3 l' s, S; R" Q. j
- INSERT INTO ta VALUES(100);4 n$ |+ \+ _4 m# r' ^
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件4 _5 @9 N/ V: H3 d( e
- */
1 K1 `/ }7 p6 V3 d -
+ a7 J5 A( q& R6 t u- O+ V. r - // 关闭错误报告
2 M' | T A% W; x) e# r. W9 @ - error_reporting(0);
7 j* d: f0 ^+ R ~+ o; H
1 N2 q! P, ]; a) e. \ E0 Q- $dbhost = 'localhost:3306'; // mysql服务器主机地址5 ~/ {7 o* k- ^( V# O7 w) g, {
- $dbuser = 'root'; // mysql用户名
: V+ j1 k$ m9 n0 P0 z - $dbpass = 'root'; // mysql用户名密码
j1 l6 ]2 i( b - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
& v% L }* D8 F) {8 m - if(! $conn ) [: Q2 g% z, \( y; g1 j
- {% y+ z! c- m6 l
- die('连接失败: ' . mysqli_error($conn));
$ Z! P! T; z1 H' e - }
6 @+ n; i& j9 I) Y1 W - // 设置编码,防止中文乱码7 A4 C) b1 @7 e% j
- mysqli_query($conn , "set names utf8");' g/ |* u: L" h1 e$ ~5 O" @: [
- mysqli_select_db( $conn, 'temp' ); $ `, K/ f3 m+ [- H- A
7 o; l: n- g( _# ]: B- # php中的文件锁 ! l- C3 C/ {9 ~9 d
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 9 j/ c5 U, o' A. {: y
- flock($fp, LOCK_EX);// 排他锁
) G8 D& @- a+ W( G3 v, v& P( | - 6 [" o( Q6 x9 w {! D
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); ( c& j5 y' r7 o# d# E
- while($row = mysqli_fetch_assoc($retval)) } A) U9 T- [; d+ V4 E; h
- {
% `; Z v0 _, ]# ? - $id = $row['id'];
, F. j' g. o0 X- p( I - }! I0 ]6 G0 l! s8 ^2 T B) g& J
- + X. L/ r/ U5 u* k
- if($id > 0) 7 j8 F( Q( `) R& x) s+ y( U5 H( e
- { 4 D6 c8 Z8 @, J9 s3 @1 {: Z
- --$id; - q. e6 a* n( i- E- F" b
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
9 W& D& J' D7 S; F j) v - } 2 I$ k$ w" Z7 ?# a0 @; `3 ^) t
- # php的文件锁,释放锁 6 q9 p3 b, F; J _+ `# V
- flock($fp, LOCK_UN);
0 f5 [, m& J5 e( ^ - fclose($fp);/ ]) X8 I( k4 W, R# K4 @+ m
5 x4 X( o- M3 m4 {7 ~: a0 Y% \- // $res = mysqli_query($conn , 'SELECT id FROM ta');, Y; n9 ~. E# s, {
- // while($row = mysqli_fetch_assoc($res))
& O$ m( e( @' u1 ~. o - // {
: ^' |# c' t6 t - // $id = $row['id'];4 s5 T/ _ d$ o" P3 A
- // }3 C8 j/ w% u3 b5 d
- // echo $id;
复制代码
6 ?" Y( { S, n0 ?" q0 a; o8 Q ]
2 J: V8 N2 ~3 e) k抢券活动实例: - public function envelopeSnatching(){
$ w! r. A+ v, o; z1 E( j! b9 ~. w - $lingqu = $_POST['type'];* p2 s+ L4 `) }: s: t: n
- $uid=session('u_id');//用户id
# j5 \$ X" ^1 {; V$ ^$ {; O' } - if(!$uid){' t T# g4 G X i% v' g
- $data['msg']='您没登录,请先登录!';/ G. Z2 Q+ u+ _
- }else if(date('Y-m-d') != '2017-12-12'){
) c- W1 C0 o* q' ~8 n - $data['msg']='不在活动时间内!';2 H, [% _1 C V+ p: U" x& r+ m
- }else{
, M7 h" i3 i% G6 Q& q) H" q5 D' R3 ` - $hours=date('H');//当前小时数
3 L. A& b1 A- O1 k) ` - if($hours > '09' || $hours > '17'){
) Z$ m X# p* G. a - : P, K" o5 d! r" h5 M1 g! i
- if($lingqu == 1 || $lingqu ==2){//点击10点的
3 n* q1 ?- O% N! k" P1 P, q - if($lingqu == 1){
! Q: ?. U# ~' E - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过7 W3 D# I" I- R8 b) h' i! i
- if($hours > '09'){1 @+ A+ P' V" H# U1 C( d7 N* b+ }' R
- $num=mt_rand(25,28);//优惠券金额1 e5 \9 g: b- v3 {" N0 h
- $id=1;) e3 s9 A# a9 ^5 w1 W+ G
- }/ M8 ~! s) c1 \. J1 S* W
- }else if($lingqu == 2){
8 w% u1 C$ Z) C3 ~5 Q - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();1 d, X6 ^5 w0 s0 o5 Q1 S1 u
- if($hours > '17'){
2 M0 A9 \3 T& W' P% j( q - $num=mt_rand(50,55);//优惠券金额5 ~: f9 R% m9 Y
- $id=2;& y# S. k9 \% j" q/ z. B5 L3 N
- } Z3 W4 o1 _9 Q+ h, L7 N5 a
- }9 N- C2 }$ N0 k7 Q
- if(!$id){/ ~8 R7 @( c+ q* a6 |
- $data['msg']='时间还没到,晚点再来吧。';
0 j$ a, V/ }% ~7 j; x i% C - }else{
5 Z' w* n- s+ y% e* f - if($is_lingqu){
$ E+ W" @% B; i9 ^ - $data['msg']='你已经领取过了,留点给别人吧!';
. J0 v: g( @8 t - }else{2 r) g7 R" I; n4 `* L$ n
- //锁表
) _" e. S2 j" f; O/ o3 \- c' a - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
8 v6 j C y4 b/ `3 Y - $active=M('active_num')->where(array('id'=>$id))->find();
/ k; L9 d- k$ `+ {; x$ P; y - if($active > 0){
- U3 x4 D$ r1 J - //开启事务
2 H7 w& g9 k E$ H2 l) L9 ?; { - M()->execute('start transaction');% M. m1 ?2 P! s# Y4 k
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));% D8 X z$ `# Z6 E
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));& y0 y) d+ i; S9 H: s
- $members_preferential = M('members_preferential');* Y5 K0 y3 s( Q* B% y& T, f; V
- //对应投资金额,1 q" p; Q! w7 I/ D( ~$ N
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));. r1 [8 k3 N1 N V$ R, } }
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');; [$ x; w4 s9 I* Y' Q1 E q
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
a! p" W4 {, \& @6 u) t) K - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券; s2 G5 B, ~$ B
- if($save && $add && $add2){
% T& u. d8 m- e. Y& c - //事务提交
3 c- k) O! s& g0 B% u: N5 S6 r$ T8 P& w7 z - M()->execute('commit');& S a5 Z2 M) r" g8 T
- $data['msg']='恭喜你领取了'.$num.'元优惠券';0 p5 s4 V# C, [! P! d( A
- }else{6 o8 {6 @& ?9 Q2 }
- //回滚3 ]1 F1 P% p' y8 L
- M()->execute('rollback');
; f- T8 y( \" f6 r1 w( [7 r - $data['msg']='未知错误!';
0 P' O2 R: ~& Z* R8 F% a( v0 Y - }" C O2 b n4 E5 q' ~* I! g
- }else{0 p2 B: A* T4 E+ Z0 f2 w
- $data['msg']='红包已领完,你来晚了!';2 b7 P7 Q$ A- q$ T8 ?. w9 w
- }# [* `* W* j' e+ P; r$ y3 S
- M()->execute('UNLOCK TABLES');
9 C* T# t$ |% i. i4 u) ?& o - }
& `, g/ f q; X9 i/ X+ o - }, g4 o3 Z. Z' @: F/ g
- }else{- P7 s7 E1 ^8 y' n+ Q
- $data['msg']='非法操作!';
, Q! u" B. F1 o& X: F4 h, T+ ? m2 a# O+ Q - }
) N. U7 V4 z. Q8 T/ Y. Q - }else{1 Z2 b5 u- |% f; z6 Y8 W
- $data['msg']='还没有到活动时间,请晚点再来哟!!';9 J9 t4 S& M- m4 `. d' F
- }
! o# w4 q1 B2 [0 a8 A1 y - }
( S0 R6 m6 Y$ @7 ]5 r% p2 ~ - exit(json_encode($data));
0 K: S. Z. C. k- c* `7 n. O - }
复制代码
( `5 {6 p9 K5 z8 ]+ ]. z) g3 }% G% A- |2 p0 d4 V# m, t4 A- `
|