模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 - [& p. l. f( r4 N. _
Mysql中的锁语法:
6 ?6 t) n, s3 w# Y) s7 Z/ X3 uLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】 H1 ?$ D2 n9 Z) [
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
4 a) v# o6 s8 p2 _" q8 gWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞; W5 ?3 Z1 |. O( q, c
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :. c1 o1 i$ Z; Z: r7 E6 j
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。1 J; a( Q9 \! `, s! y& F0 \
测试时,有个文件就行,叫什么名无所谓 总结:
, d* [ F- K, e7 A* m. _. A项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:$ Q1 I4 Z) W% I2 P/ t
1. 高并发下单时,减库存量时要加锁/ Q: g) D" K$ u
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*1 N) s" Y4 x& t: \' {9 K; [
- 模拟秒杀活动-- 商品100件
, ?8 b0 K E& O2 w$ |# B5 t) p, s - CREATE TABLE ta
6 z/ d0 w ]$ v; A - (1 J7 D0 m: [0 M! P/ W
- id int comment '模拟100件活动商品的数量'$ D/ V: t" V- o- |+ J
- );5 Z# p; {: e9 Z, S9 Z
- INSERT INTO ta VALUES(100);8 K3 N4 B! B. {6 Z2 w% T
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
* @* Q! }- g8 Y/ r% X/ |$ a - */ + ^) n& Z9 {* \# D+ i
- - v/ X. h$ h1 Z$ c6 c2 Q6 F4 @/ h
- // 关闭错误报告
" x; {; e& @* A' d d- g; O8 { - error_reporting(0); 0 ~( S: H: i% n$ o- p/ _) n
- $ n5 Q7 O6 G. t- v( }# N
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
+ P+ A5 k7 [. t. e( v4 m1 i2 \% Z: @- | - $dbuser = 'root'; // mysql用户名" g3 F! H9 {9 \1 s- [( R' g
- $dbpass = 'root'; // mysql用户名密码
0 y' }. G6 Z# } - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);$ A Y! C" j2 f8 f
- if(! $conn )5 M& ]! ^8 p9 {$ [
- {1 w. W: G/ Q' J: t$ t$ h* w3 ?
- die('连接失败: ' . mysqli_error($conn));1 g! w; k+ d2 Z- `. W1 @
- }0 B+ T1 w/ ~$ M$ {+ z
- // 设置编码,防止中文乱码7 T9 D9 j! }: A7 `: E6 h' y
- mysqli_query($conn , "set names utf8");$ W0 W/ P9 ^$ B9 t5 N
- mysqli_select_db( $conn, 'temp' ); % I* w3 [/ |- D/ W, ?
* W1 t; U9 D) O" L9 o- # mysql 锁 6 ~- X+ \6 ~3 v) G$ i1 R* s
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
6 h3 I. ~/ L+ A; B - $rs = mysqli_query($conn , 'SELECT id FROM a');
0 o( ~; y; |# v# k% n' o - $id = mysqli_result($rs, 0, 0); 9 R0 \. m" I* K0 i7 N- F: M- C
- if($id > 0) 9 Y+ z& B3 E# \) F: P" N5 y
- { 1 f4 R, |) ]# v# `- x
- --$id; $ Y0 n8 v+ [9 G1 _, ]
- mysqli_query($conn , 'UPDATE a SET id='.$id);
' ]2 q+ K0 F& A5 ?4 [* b' R- f - } 6 s9 x1 O+ W( H9 W
8 U0 z1 G+ P! Y1 I0 P; |- # mysql 解锁
# e' g; @! g2 u3 X) x; c - mysqli_query($conn , 'UNLOCK TABLES');
- T. [: V$ R1 M( s# Q - //查询解锁后的id值
5 s% f; u9 ?/ M' j0 g8 L - // $res = mysqli_query($conn , 'SELECT id FROM ta');
2 P' x) ?2 v- t- q' S8 y - // while($row = mysqli_fetch_assoc($res)) d/ y7 G6 ?4 L: o+ {
- // {3 D# a9 |9 I9 _" B# N
- // $id = $row['id'];* G$ S3 W$ w9 q
- // }
- Y; P4 w6 f$ w+ |/ I& `3 ^1 i - // echo $id;
复制代码 $ F& S3 h2 F. z; q. A! g- o
% s9 K2 O5 g/ C& r3 Z! ^) t0 o) ?+ J
PHP文件锁示例: - /*9 x8 R! W; `" \5 I0 p
- 模拟秒杀活动-- 商品100件) G! m4 k( R% g' M0 Q7 t
- CREATE TABLE ta( {9 D. u4 s6 U( E$ g; t
- (
& I2 ?/ T. C+ ^$ l% u - id int comment '模拟100件活动商品的数量'
, _0 s- k" f# V0 S - );3 t7 g0 W& T# \6 ]/ u$ r
- INSERT INTO ta VALUES(100);
7 P' `# c0 q" @5 c1 q7 N& E - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件! E; }) g* ~- {, E( A: V5 L S. ?6 w
- */
( ?# C. `- f! z6 Z# } -
* P, X6 H0 f$ g - // 关闭错误报告, m( ^5 X T0 X! Y: n, e
- error_reporting(0);
2 N3 D( l- |7 t) T
& g$ g* H0 @% O4 D: C- $dbhost = 'localhost:3306'; // mysql服务器主机地址/ a3 M- h8 ]& n. E5 b
- $dbuser = 'root'; // mysql用户名 J; J" \4 D0 B; Y0 d
- $dbpass = 'root'; // mysql用户名密码- X4 ]# O$ Q0 P! `
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);( d0 F( W: B+ E/ K& D5 I' A/ d, L
- if(! $conn )
9 q$ _ i; s# V# ?/ }+ B; T: f6 |: x - {
8 H! S9 m/ [% n. j& K0 q6 N% a - die('连接失败: ' . mysqli_error($conn));- a3 p5 G i; R6 V) P; A+ K
- }
, S+ `6 Y4 \* i) |5 U' h# J% e/ l - // 设置编码,防止中文乱码
5 ?8 _3 v( @7 l) V% M+ E - mysqli_query($conn , "set names utf8");1 u5 j- i/ ~" I1 a3 J0 d3 d
- mysqli_select_db( $conn, 'temp' ); 5 J. r/ J' P$ }* a
; k. a+ a/ M; j; x2 K6 Y0 @- # php中的文件锁
& w2 x2 [% n# @: D" W: I1 x! }8 I: M - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 4 m3 I' |" I1 X( H2 H
- flock($fp, LOCK_EX);// 排他锁 1 E$ H3 n& D" K c3 C
0 F! g$ G9 [; `; a. s0 D0 X9 q* T- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 5 K8 h2 |, H( |7 k; N1 x* A
- while($row = mysqli_fetch_assoc($retval))
( k8 w; j0 Q! y5 Z - {
7 y J$ }- ?- m$ }) C5 m* Z+ o - $id = $row['id'];
; V! F& j; t6 @ - }
: x+ v* p1 ^/ G: n0 g4 b. w6 L -
+ Q/ P4 r3 Z0 ?! k7 v - if($id > 0)
5 t8 g% d9 v# T% w7 |3 p - {
) v$ p: g8 A% T/ M - --$id;
/ y9 }- X/ Y+ x' ~2 A - mysqli_query($conn ,'UPDATE ta SET id='.$id); . J# @$ F7 {5 e8 p- i
- } 3 f. q' g4 p, \6 U9 d' X( e. U
- # php的文件锁,释放锁
. ^. R: W. C0 i, d: s$ C - flock($fp, LOCK_UN); * ^! t) A, Q7 F h" T% m
- fclose($fp);% R( O! P, M# F# k
/ ?9 n' R' I, }) H }; l- // $res = mysqli_query($conn , 'SELECT id FROM ta');1 R, G) P+ q5 @9 `# E) o E! H# C
- // while($row = mysqli_fetch_assoc($res))
t5 {5 [1 f. U$ b/ @. v, Q - // {
8 A, R* B# L" t6 a" p) M - // $id = $row['id'];
. k' H+ ~/ E: ?, } - // }5 p8 w7 n- i3 i* M3 g! Z1 }
- // echo $id;
复制代码 & t* b4 o0 X1 N% g* a& T S
* Z8 o G" U' N5 G/ A2 g0 A
抢券活动实例: - public function envelopeSnatching(){
( c( g5 _3 @" K" [) b - $lingqu = $_POST['type'];6 q; K1 D3 G" g% D& B& w: D" b& J
- $uid=session('u_id');//用户id
" m) j3 n( O5 M- V - if(!$uid){& J' b" u9 v* R. H
- $data['msg']='您没登录,请先登录!';& L& b9 {% O) x W
- }else if(date('Y-m-d') != '2017-12-12'){8 s Q: e: H3 G& \; G
- $data['msg']='不在活动时间内!';, K+ L/ F! G- ^
- }else{8 B) T5 N" f# L) y. j! [ ?" p
- $hours=date('H');//当前小时数, P) {0 \) J# @8 K8 h7 P9 f' x8 \
- if($hours > '09' || $hours > '17'){
% A6 J- M3 _2 c& @2 C" \' C - 5 ?( W7 ^$ `5 ?0 L, j+ f9 \4 Y
- if($lingqu == 1 || $lingqu ==2){//点击10点的
& o: `' s% P: k! `; x/ E! b9 ~ - if($lingqu == 1){4 P' r/ t. G5 I9 D; Y& a
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
7 S, V" Q p1 g - if($hours > '09'){+ Q4 v9 v7 U' ^0 |- M4 V
- $num=mt_rand(25,28);//优惠券金额
1 j- f' v3 Y. f0 o5 n - $id=1;
6 s$ F. [' @4 E+ u# k - }/ a7 s+ ~, i' E: t. Q" S8 U
- }else if($lingqu == 2){
/ i) U1 U& [+ H1 P! } - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();# e. t# m, R- E4 N0 f1 s9 s
- if($hours > '17'){
2 H' B; o8 o% R3 T - $num=mt_rand(50,55);//优惠券金额+ O5 M1 a- g$ w0 \& d
- $id=2;
7 ?- K8 r; z$ a3 X - }
7 D& B- ?% n! J7 m$ _) G - }. j7 G8 P+ R6 i% \, s. t, O
- if(!$id){$ }8 ^/ W1 j1 u! W( K) F7 q- w
- $data['msg']='时间还没到,晚点再来吧。';
# _: w. ]/ G9 g. g& Z: F& i% r - }else{: Q4 k0 v. C# h0 P- q
- if($is_lingqu){
1 z* B' Y* L- ]; q) K - $data['msg']='你已经领取过了,留点给别人吧!';
" C0 T( X& O& N! n, _3 [, T - }else{
( p6 C: h3 V% E0 B3 D; i - //锁表
: D' o( A5 E# P$ Q" m( Y/ ` - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');5 y( ?8 g0 U" a. O" ~% W2 }+ l0 i
- $active=M('active_num')->where(array('id'=>$id))->find();" H5 e! T: x0 j' j' }6 M" n- g
- if($active > 0){
) O& B0 h. t5 v7 U+ n - //开启事务
" W1 k$ U' J b* d - M()->execute('start transaction');. h2 b4 E% d1 ^# Z& a
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
E* @( }4 x0 E* c1 s% l: n - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));3 T0 O: n5 k: B9 P
- $members_preferential = M('members_preferential');
! K; [& R0 j& N/ t* h - //对应投资金额,1 K$ `: R0 X2 A
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
1 g. O% a" g% ?9 x - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
- R2 U0 o0 G z/ r; v2 m' u - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
+ {% q# G8 T; Y - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券" I0 r3 R t! W$ C" T4 X
- if($save && $add && $add2){4 S1 U- \1 z1 g7 N; F
- //事务提交
: h4 ^8 v3 f$ r- X0 l- l- g - M()->execute('commit');! P# _( {" _9 m/ ~; h
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
4 { y" t9 ^3 ]. D$ X0 n - }else{, A6 r& M: @5 R& W
- //回滚7 ?9 m6 @: K9 e( e
- M()->execute('rollback');2 \; C# N+ }3 T: P8 V
- $data['msg']='未知错误!';1 P6 X, K9 I& j9 g
- }. v! z: ]( d) ? f7 O$ v, X; L
- }else{
9 p7 N+ G, ~* F/ x2 \; R - $data['msg']='红包已领完,你来晚了!';3 {) d; P0 w9 Z5 k4 ~
- }
- ]* U/ H0 m) x/ D! x6 g- @ - M()->execute('UNLOCK TABLES');0 j8 |1 n V; q8 j1 J; f
- }
) h$ h( W9 N9 p2 M$ E# O, O - }" f( H9 g2 I6 j. ?: W
- }else{
, ~; l) J) S. V4 E! u0 d - $data['msg']='非法操作!';7 \! s/ E& G$ Y
- }. |3 Q) U; z6 {# z
- }else{, f! w. K% E5 w8 k
- $data['msg']='还没有到活动时间,请晚点再来哟!!';: H( m O' l$ }) ]( {
- }4 e4 Z. l* i' d E3 U# @% N2 ], w
- }
: n$ I' z( M' H0 L4 X7 f; h - exit(json_encode($data));3 c; B/ j/ `( t
- }
复制代码 6 C/ V9 s m0 ]# T6 i
4 [4 S C: S- ~" O" B! ~. f* W; G |