模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 " u% ?" }" n* v7 D1 r! y8 D
Mysql中的锁语法:0 {5 a) G+ p7 F. t- \$ M
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
$ ` h8 K3 ~& \: h0 M4 A* SUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表+ ` X1 p# l% w0 X& g4 v& v
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
7 P9 m2 n! {" ?: A, ~注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :: g( y$ D2 O7 n/ w$ ^" j- I
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
: S, J* r. Z$ g5 q4 U+ |0 L) C测试时,有个文件就行,叫什么名无所谓 总结:/ T, a) w, w6 u1 D& D; J
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
: J' f# {7 z) G) B" J, S1 \- Q* s4 _1. 高并发下单时,减库存量时要加锁7 ?1 N- w. a) [
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
: ~/ P6 S$ U+ l4 \# R/ d - 模拟秒杀活动-- 商品100件1 f4 S) N8 @" T& ]: d# ]7 ?
- CREATE TABLE ta: q" q$ A& `8 l. @$ @
- (
1 f3 V. ~" {; a- f - id int comment '模拟100件活动商品的数量'3 w! d) [' t& w6 n: c& R4 l8 x5 B/ H
- );8 `* u- P6 G6 Q# a
- INSERT INTO ta VALUES(100);
+ D+ S3 D; O7 V8 z" Z7 A# M - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件9 q* }7 M4 D d6 e2 L
- */ + ]. r1 Y: m2 @+ j" \# J6 ]
-
/ s* Y- o& N* j. J5 K - // 关闭错误报告: y) F8 n7 C! t w
- error_reporting(0); , u' M, O0 o- i
- 2 ^1 T" i0 |% _; I+ q: p
- $dbhost = 'localhost:3306'; // mysql服务器主机地址7 F. U% }# u4 [" C8 o4 E& _- W
- $dbuser = 'root'; // mysql用户名8 }$ e' `" _3 z4 v/ i
- $dbpass = 'root'; // mysql用户名密码
( T# G6 |. P, }( h& E! ?) w. B - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
% p0 w* r6 i4 }5 k- ]+ Z0 I* [ - if(! $conn )
0 y- c# R D8 f# ~0 g# u/ I% g - {$ G0 r. d) Q) C
- die('连接失败: ' . mysqli_error($conn));+ M5 D; z' m% X1 T4 B) `
- }
: j3 F8 |4 r; W9 | - // 设置编码,防止中文乱码
W9 v$ E1 @6 e9 X - mysqli_query($conn , "set names utf8");; h! [# i/ C! R" e! j' ~
- mysqli_select_db( $conn, 'temp' ); 2 i" T8 P) V3 J" N/ q! Z2 B" ~
- , ^6 B' W& ?- \7 k; [
- # mysql 锁
0 w" |" Z; V+ t9 a4 M+ B - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
' v& P; D, Z6 m) _1 R3 ] - $rs = mysqli_query($conn , 'SELECT id FROM a');
7 x) q% |; o& X/ N( s - $id = mysqli_result($rs, 0, 0); ( x; k, E" k$ E7 s7 q5 N, C
- if($id > 0)
* b! N' V/ i/ B0 r/ M. F; M - {
8 D, t8 T. ?3 K( K4 k1 I a+ d - --$id;
/ J Y' N" K) Y8 C5 L3 p - mysqli_query($conn , 'UPDATE a SET id='.$id);
. Z: x1 W% [" z6 _) a: I$ a - }
; Y# H; \8 \! j' W6 e& _
* S% }5 G+ `% _) v: R- # mysql 解锁
' m( E- ~% W% _ P% H - mysqli_query($conn , 'UNLOCK TABLES');( {. _$ h( \7 x" [. n, r& i
- //查询解锁后的id值
9 [4 i4 c6 h0 n. v - // $res = mysqli_query($conn , 'SELECT id FROM ta');. m) }1 x% _$ T) T: j+ @
- // while($row = mysqli_fetch_assoc($res))
/ V( G/ T5 }, C0 ?7 a0 Q& ?, A - // { F" y: B( X" G, i3 y2 a V
- // $id = $row['id'];
1 R m' G9 |9 S1 h5 d% K - // }
/ x7 n* e$ D4 D2 L3 |+ q+ E - // echo $id;
复制代码 3 g3 j. ]/ G, ~4 W( G
% O0 V. r" y" g/ P' [- k
0 I3 Y8 N' \) V$ B7 b- `PHP文件锁示例: - /** s9 b- \. p# e( t/ `
- 模拟秒杀活动-- 商品100件
. e' F7 C( r2 e s' e8 L' R6 V - CREATE TABLE ta
# X y+ T1 q/ P* Y: ~ - (8 }0 V6 s/ F- p' q t: \. i$ @& z8 Y
- id int comment '模拟100件活动商品的数量'
; a# ^1 d3 f* Z! F) e3 a; A - );3 S4 a* I5 p) R9 f+ u9 k
- INSERT INTO ta VALUES(100);
! C- S0 E/ z4 A, \' Y - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件% A- O7 e4 h0 g7 t& s- i" A6 b
- */
9 @( G R/ g. S7 o" k - / k* i, R; {* p/ Q" e
- // 关闭错误报告
7 r; [+ A( |1 w - error_reporting(0);
4 {, ^# [ @0 U" n# x* X5 `" k - . S5 s+ a3 P- Z4 e# D6 ?1 }
- $dbhost = 'localhost:3306'; // mysql服务器主机地址" x' S- Z# n% i/ ~, k
- $dbuser = 'root'; // mysql用户名, s* J# |7 `2 K+ X
- $dbpass = 'root'; // mysql用户名密码
# E6 s0 r4 U7 m9 c Q' [ - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);3 r5 J" A. i, P0 s
- if(! $conn ), z+ o, D- S- Z' n0 w% L
- {
1 E* |2 h- n4 |5 V" o - die('连接失败: ' . mysqli_error($conn));' m% X$ c# k9 ~- w
- }+ t B+ x# G( U' Z: j. G
- // 设置编码,防止中文乱码
( h) V4 H8 ^8 k. @ |2 } - mysqli_query($conn , "set names utf8");" z3 I) x y8 @" a/ C$ i
- mysqli_select_db( $conn, 'temp' ); $ L2 D" F3 u9 b6 O* ?7 T
! D8 P& i) N0 V3 U2 L; _; N$ F- # php中的文件锁
7 b& p L6 n' I! E3 _ - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 ( E% L7 M5 }/ {2 b) x
- flock($fp, LOCK_EX);// 排他锁
8 ~, p: v; M6 y1 `( d+ I2 {9 A - ' I0 e v( [# D2 O% B6 l4 I3 m' ^; _
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
6 z6 c8 s$ h) d7 B- g, Q - while($row = mysqli_fetch_assoc($retval))$ u5 n, A' g7 B- D1 V
- {0 P- j' t" _0 }# ?" D2 u) M; u" S
- $id = $row['id'];
3 E4 _$ G5 N9 t - }3 G1 }! i/ K& t' u; t' H
- : H+ v! A; e+ _7 p! Z
- if($id > 0)
$ i7 A4 d$ V" M* I( u: H - {
* X4 M" V& e: f0 a1 J; e1 W1 R6 L4 u - --$id; ' e% {5 W6 a v1 A* |! K
- mysqli_query($conn ,'UPDATE ta SET id='.$id); K4 B5 p' F& T K1 z
- } / g& W4 Q- H# Z. ?7 j2 r7 O
- # php的文件锁,释放锁
$ G: B4 Q$ _4 [1 F3 y5 ?0 ] - flock($fp, LOCK_UN);
6 o, |% u! b1 x" a# v8 [ - fclose($fp); B+ d* d- e% S$ k
# {# V0 v# G# Q+ k4 L- // $res = mysqli_query($conn , 'SELECT id FROM ta');
8 @1 a: d- }6 R8 u8 ^7 ^4 I - // while($row = mysqli_fetch_assoc($res))
1 x8 W/ `, ~& t8 v4 a - // {
3 r( c1 Y1 S$ Q/ Q7 Z) U" k7 q/ R - // $id = $row['id'];" r5 W! Q4 g7 Z1 w- W
- // }
5 F$ J. ?; p; B1 T - // echo $id;
复制代码 . j$ T+ ^- Z' H" j
' J D' b9 v6 q( H% T7 m: }抢券活动实例: - public function envelopeSnatching(){
) u0 }+ {+ ?5 F. \$ O x+ N - $lingqu = $_POST['type'];4 b: @, o5 D% `) ?
- $uid=session('u_id');//用户id
, a% u" a( }, Y7 K4 F2 `3 Z& d - if(!$uid){" w( C2 r% J, d' Y" h) }& `
- $data['msg']='您没登录,请先登录!';
9 x+ u& e5 Z) K" k1 ~' [ - }else if(date('Y-m-d') != '2017-12-12'){% t& m2 M5 x( |3 _* e# ^- C
- $data['msg']='不在活动时间内!';! p( Z1 d4 C7 Z+ L; b
- }else{
0 D( L& v# f0 T - $hours=date('H');//当前小时数
2 c# E2 x$ y z7 n8 [ - if($hours > '09' || $hours > '17'){, C3 w, j1 ~6 Y7 a! K
- 1 u6 h' l7 _9 N; T/ ~) k7 m
- if($lingqu == 1 || $lingqu ==2){//点击10点的- ^, E, w/ c# |
- if($lingqu == 1){
& M" x! a) M* D' d' ~. d0 | - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
' U. s. ]& S( T+ r: u3 a) b - if($hours > '09'){
3 k" u I9 X& }; t6 N8 z - $num=mt_rand(25,28);//优惠券金额
3 H3 f$ Q4 ~4 O' S - $id=1;
' B X# d( ~: w( | - }# x. P3 H7 r5 e) W( [; h, R7 Q/ A
- }else if($lingqu == 2){
2 Y3 [7 w: k6 u6 Z/ s2 |+ L+ ]4 d - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();; L3 e# `2 C/ F! j- Y
- if($hours > '17'){
* s! G& ^5 P H: c9 F- I& j - $num=mt_rand(50,55);//优惠券金额9 u$ W( i5 j% S# u+ G9 B" t a
- $id=2;- p, ?- P& e1 p" R+ S
- }: h+ @1 A" G# w. g; [1 Q, Z
- }
. V; `1 b6 V4 x$ S - if(!$id){+ d8 t9 u8 O7 I; n8 {, E/ o
- $data['msg']='时间还没到,晚点再来吧。';" d# g- m' W9 Y6 p$ f
- }else{8 y X. D$ v' }( y$ h7 ]) W
- if($is_lingqu){. C( [' L8 f O7 [$ G2 U
- $data['msg']='你已经领取过了,留点给别人吧!';8 |9 {" C2 W5 L" ~
- }else{
* B" _, g; g9 z T# g k: u - //锁表, r* d; i7 H M3 c$ J `
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');; N2 d3 U( c6 O
- $active=M('active_num')->where(array('id'=>$id))->find();
e" \0 ~" b3 c: y [ - if($active > 0){8 S* ~; }) j+ ^2 [2 y* g$ e
- //开启事务. H t1 h2 L3 J5 R& a
- M()->execute('start transaction'); [3 }$ j! q' ~% c# c _2 J
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));$ Q' M% ]: o" o. R+ m, a6 J) g
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));+ n, A' I$ Y' J: z* e1 I
- $members_preferential = M('members_preferential');3 K$ _0 w" R& k& @3 j+ U6 {
- //对应投资金额,( R% l% b) ]/ |% b
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
( w" t P4 ?2 i7 a* R/ L- ~ - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
7 m) ]: |6 r1 Y# u0 E1 s - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
& \, m; ?, [ @/ X# m \ - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
! j' ?) y: |# f; K - if($save && $add && $add2){
t a% l- W5 {7 [% F$ ` - //事务提交 H. [ h) t: j5 E
- M()->execute('commit');9 w L' }$ B; b/ ]; t9 ?7 X* M
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
8 p! C8 \2 ]+ U a r4 O4 z - }else{
0 o( k. K8 a8 ]# N d. I. c5 y2 r+ ` - //回滚0 m6 H/ Z4 c! ?3 a; d; ?
- M()->execute('rollback');9 a; d7 ?! b: m4 C; c1 m( k
- $data['msg']='未知错误!';: F$ q: q- S e
- }
* a( R }+ F5 a# e; ^ - }else{- o6 _. q+ i! P5 O* w, U% R: }4 O3 r/ i
- $data['msg']='红包已领完,你来晚了!';
# |0 S, e! l: l a4 d8 l - }
! Y @& b) ^1 j( _* x - M()->execute('UNLOCK TABLES');! p9 O3 c& A3 l: O) k, |% ]$ N% O
- } S3 ^+ }" @2 O6 B, ?$ z9 r' ^
- }
6 D# }: z/ L0 _: s6 e5 q7 U - }else{
" ~% z7 h: W C. f- x - $data['msg']='非法操作!';% l! V9 }& T) A" ^
- }
( }; {* p" [9 s7 e - }else{
9 @& D& k. s+ i: o! a( r. p, J - $data['msg']='还没有到活动时间,请晚点再来哟!!';( D3 Z! C- K4 S& _# ?* {7 Z1 F
- }: [3 o) U( \; i3 J
- }
6 E3 k9 O2 e$ n8 S/ n( P - exit(json_encode($data));, h g3 x, C- q1 M# [
- }
复制代码
) F: ~( u' K; Y3 n# y: G d& h/ U/ v& t
|