|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 ( {5 l* z# D, j
Mysql中的锁语法:
2 A ^ `) Q$ [, F/ {, ?2 fLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
/ i- I8 X9 z+ ?UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表4 G7 K/ }1 G7 a% a' c! O
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞 D o6 s) Y I' S$ Z
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :; \/ Y) S. p& T' U* a" `6 _
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。& I9 q0 l9 g4 ~8 L9 P
测试时,有个文件就行,叫什么名无所谓 总结:
5 p: \$ j5 _$ g. U# v: V5 s ^项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:; M/ F) B5 M. M% |: k8 O' V
1. 高并发下单时,减库存量时要加锁
/ j' M8 E& Y. o% y j5 n5 w2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
) g5 a0 U* }3 S6 I - 模拟秒杀活动-- 商品100件
- N! R) E' }, W6 K3 K' Q - CREATE TABLE ta
4 L E/ }3 [& I$ h4 q" } - (# L; G% O" j+ w( e/ L+ M- Q+ T
- id int comment '模拟100件活动商品的数量'% o0 m9 g2 l4 _5 \0 @4 d
- );! T3 M0 R8 s* N. B$ ^
- INSERT INTO ta VALUES(100);' N; h' y9 d6 H* _! m
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件* w& B* `: c9 j ?& K7 Q0 C# d
- */
% c# o, c0 m) r+ p* E; H' D - ) ?9 x6 s! ?$ P) a% ]( S; g
- // 关闭错误报告' \ F% Y( i! H* `, \" y% A9 N
- error_reporting(0);
& }% i- A6 i/ v6 T
4 S" q+ B& R3 D$ `9 W% ^- $dbhost = 'localhost:3306'; // mysql服务器主机地址% l+ B: q. h; J% J8 _/ j
- $dbuser = 'root'; // mysql用户名! e; d: U7 e. n, y0 J- k+ l
- $dbpass = 'root'; // mysql用户名密码6 t$ R6 h0 h1 M* y$ E& p4 b1 e5 Y, h6 t# T
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);4 f2 H) N# p. ^
- if(! $conn )
0 |$ o" P* c9 `, }8 x - {! b5 I! q2 Q! T. a7 z: p5 `9 B0 |& @
- die('连接失败: ' . mysqli_error($conn));. H0 M. m; F% C
- }5 O! F) q! [9 C. Z
- // 设置编码,防止中文乱码
; n0 a' o& j ]4 s& }* D - mysqli_query($conn , "set names utf8"); o. M9 Z: F. F. X2 P3 |& L8 g r
- mysqli_select_db( $conn, 'temp' );
: r% p' W7 c) f* ]1 R% q% R - 8 B+ m/ }' H S- a8 T6 u) y
- # mysql 锁 $ B# H9 y* R3 X: | Y, M& B2 ]8 {8 d
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
8 D. R2 G$ u% _! k* g5 {* k2 k$ _9 d - $rs = mysqli_query($conn , 'SELECT id FROM a'); # D( n1 Z3 v" x. t7 V7 R' O# N* b
- $id = mysqli_result($rs, 0, 0); " ?4 G/ G& F! |* x% c) X8 \
- if($id > 0)
b( J, F1 R0 i - {
0 T$ {+ y; N! u E( n0 Z - --$id;
) ?# `4 x' q" e/ S5 _8 ~( n- F - mysqli_query($conn , 'UPDATE a SET id='.$id);
, B) ]/ t; i' T2 H$ ^+ j$ v. r - } ' s- s+ B3 u% x( H& ]' U
$ P# h' i; g0 [' B2 J9 m% u. g$ B- # mysql 解锁 7 [4 K, j* [0 R* B+ L6 l
- mysqli_query($conn , 'UNLOCK TABLES');3 R, L& H' g& S, {+ E9 @# `4 z
- //查询解锁后的id值
# e! z1 R B4 I - // $res = mysqli_query($conn , 'SELECT id FROM ta');7 Q' Z. G2 u" K, z8 ? Z$ z
- // while($row = mysqli_fetch_assoc($res))
" {( m! y# `" W - // {3 O9 X; F1 K7 Q: ~
- // $id = $row['id'];
F; o: I8 B& R; u8 c! ~ - // }8 q8 o# Z5 q, \2 Y
- // echo $id;
复制代码 7 y- A2 `# i$ J
2 _3 D6 R$ A+ r( ]( i4 u, q+ u3 K- L
# k6 s y% f2 c
PHP文件锁示例: - /*. X+ Y9 E! ~" l) g# i5 x
- 模拟秒杀活动-- 商品100件* G# H9 E$ N) F5 q# @
- CREATE TABLE ta
+ P$ B; c" C8 I, z - (/ D2 Y, q* V4 k. U3 E& f+ p
- id int comment '模拟100件活动商品的数量'
+ G$ V, d" q( g2 T - );
. A' ]8 y! o C ~. F5 f5 | - INSERT INTO ta VALUES(100);
8 o' e( M5 O) m/ o- ~+ r - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
. ~: W8 i: V* B, d; ] - */ ! N/ c) F/ p: s; ^' M
- 3 F5 S$ b2 p9 s/ Y: V; l7 W5 g
- // 关闭错误报告
5 } \. W) J: n1 {: w - error_reporting(0); ! ?. N! }$ I4 a! y/ t
: ]/ M- Y. _6 h) d* X9 ~ V( W- $dbhost = 'localhost:3306'; // mysql服务器主机地址% B% d5 A, ?0 k6 k1 G
- $dbuser = 'root'; // mysql用户名
7 m, H$ s- m' o8 }$ T - $dbpass = 'root'; // mysql用户名密码$ Q4 s( n9 E7 @) Q5 o6 g
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);# r5 X3 x0 b. ?3 u! t. G/ C8 @
- if(! $conn ) d0 ^# h% G' l. u9 n1 D
- {
: ]! t" C" a8 |. ?- }$ Q - die('连接失败: ' . mysqli_error($conn));
0 Y7 ?9 a6 s0 N7 b5 I - }' F; v) D3 F0 N* }; r
- // 设置编码,防止中文乱码! v9 R3 v8 V! H; F+ [0 k6 r
- mysqli_query($conn , "set names utf8");- {, m0 @/ ~2 v/ _7 r7 z+ T
- mysqli_select_db( $conn, 'temp' ); # k- y$ k2 L4 B% N1 |1 G+ V
- 5 K4 C( n8 J# E: s3 f+ }& @* `. g
- # php中的文件锁
5 |+ m j: ^1 i- I& t8 \ - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 ! _' F3 v! E \$ S* [1 R/ H# A
- flock($fp, LOCK_EX);// 排他锁 - T- \3 [' f. l) }& M
0 T: K9 @) N' Q: a6 R- $retval = mysqli_query($conn ,'SELECT id FROM ta'); i% T6 d5 ~9 ^
- while($row = mysqli_fetch_assoc($retval)). R4 Y( k# d) s7 _/ f- q1 r0 O% K
- {
8 q$ Y, y4 N8 A" g7 Y# N. s" q( ` - $id = $row['id'];
0 V) L5 u9 }7 z3 f% D) q - }
9 L, z! b* I/ J1 ~" B4 W4 @! J, i - + `& r% k4 a. n* d, a
- if($id > 0) 2 D G& @5 ?9 b$ i( L d2 w' p
- { , Y, `, e3 H, U: ]5 H" h5 p
- --$id; / m( S, ^5 ^# h O; `$ _$ R& h8 p
- mysqli_query($conn ,'UPDATE ta SET id='.$id); 7 m9 Y9 p0 G5 E8 d
- } 4 y G1 x, S, a
- # php的文件锁,释放锁
9 i$ L" s, b; }$ y* [; x - flock($fp, LOCK_UN);
3 E6 {* n2 H% c% v5 Y$ C i# L - fclose($fp);
& }# c1 Y3 r( D P7 w+ T. ? - 5 ^+ d. w2 p( v+ R
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
" P; D! p. K4 {5 U0 k - // while($row = mysqli_fetch_assoc($res)) }8 M* u+ P8 N. j! Q
- // {2 U! k1 A! ^% u( T0 u& W
- // $id = $row['id'];0 ?- c; t2 A5 L. u, U6 N
- // }) G z' [2 |( d6 e( Y8 X# y' R
- // echo $id;
复制代码 0 v, I: _4 c8 W( F
$ f4 ~- ]6 O& \' W/ g3 n
抢券活动实例: - public function envelopeSnatching(){
% s# w9 A* P1 G* P - $lingqu = $_POST['type'];
9 {. U1 m& K3 R - $uid=session('u_id');//用户id
% J! @. x0 e; c - if(!$uid){
2 y0 @' g* O0 z* U: G4 s8 O1 s - $data['msg']='您没登录,请先登录!';
; w8 e1 T9 I- X+ z- i3 ^ - }else if(date('Y-m-d') != '2017-12-12'){) }" y2 x, i2 \+ d$ v ^4 E
- $data['msg']='不在活动时间内!';
8 u6 ^2 g; R0 ^4 Z8 V! [% H; M1 N - }else{" b2 f$ i) `; u' _0 J
- $hours=date('H');//当前小时数# h2 ^" I; z+ o+ l
- if($hours > '09' || $hours > '17'){$ I% f! r3 e% F/ V" J
# ^6 L5 e4 Z8 L- K- J7 K' F- if($lingqu == 1 || $lingqu ==2){//点击10点的
; R5 b, u0 K; v - if($lingqu == 1){% `6 _5 i! f) S* n/ g, e
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过7 m) |# P3 ~% ~) b
- if($hours > '09'){+ u, D, _! r& G. Y/ v5 B
- $num=mt_rand(25,28);//优惠券金额
6 v1 K. s4 \! K0 r5 ~ - $id=1;( `/ E7 y9 T+ F- C. r9 Q
- }
; [# a3 L, y9 m - }else if($lingqu == 2){
* [$ l r; u7 U. b% ]6 h. j; c - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
0 o' ~) c- b* [5 r: ?4 l - if($hours > '17'){ D3 c9 r# X" t9 ^/ z# _# i
- $num=mt_rand(50,55);//优惠券金额
: E1 A6 x% c5 } - $id=2;$ `1 }7 n5 N3 l1 q
- }
: ]* W0 i- h8 M, A( s. ?8 K! [ - }9 N8 j! O/ G) @
- if(!$id){
: x W: B q ?. `, @: t - $data['msg']='时间还没到,晚点再来吧。';, f2 u( g: H0 o! t
- }else{9 a$ j% |% D/ y+ ~$ b
- if($is_lingqu){
- M8 A' x) X; V3 x; }! D- j - $data['msg']='你已经领取过了,留点给别人吧!';
3 F' k& e: A9 s" y3 H7 a' _ R, Y7 U - }else{' [ [! h1 h' o0 X$ a3 J9 w
- //锁表# Z) J) T) R9 l# @* Y9 b: C% D
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');1 U% |1 q+ W* m) R
- $active=M('active_num')->where(array('id'=>$id))->find();
3 B" m- j3 J9 P ~* o1 H - if($active > 0){
8 ~8 j" [) g) ?& O - //开启事务& c- Y$ C( q, ?
- M()->execute('start transaction');
$ B P6 z2 B; A& I, Y - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));6 w/ C' B' x* g- J+ X& l0 L+ I
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));+ I# z: @. z# g, |+ |! u: h
- $members_preferential = M('members_preferential');/ D$ [, f, I3 A; z- q
- //对应投资金额,2 w; ^) `, a5 a
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
d/ N" [4 J8 } - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
* ?/ F! q* `7 G9 p1 c - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间& \9 g" K* q4 R C2 t7 S7 ^" g
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
3 \, `! L) Q) u5 N$ O - if($save && $add && $add2){7 X8 M G6 H3 I Y' g
- //事务提交
# E9 [" X) b$ e5 n6 f - M()->execute('commit');+ L! C2 [- r7 e; m" \
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
% ] s0 Z( Q% B - }else{' c) {6 i: `/ I" `3 m7 N
- //回滚
/ N6 \7 I' L% S. S - M()->execute('rollback');2 t' `3 z, k# w4 |/ g/ M0 v
- $data['msg']='未知错误!';
8 t4 q( s8 r# i8 _5 S) s M7 A7 j - }
' ^7 n! I8 G- @* k; w- U( X - }else{0 q: V9 S$ {$ F
- $data['msg']='红包已领完,你来晚了!';; \1 {9 c( n' F2 X* e
- }1 ]* s" e' i8 o) G( P5 R
- M()->execute('UNLOCK TABLES');
8 t/ ~, w, U: M, h - }
3 ]( I J, F% d7 O) e+ d# R( D, C9 X - }5 O) Z3 |& z9 X. I' v
- }else{
/ d$ a) s1 { a3 e( x% h2 d% v. n - $data['msg']='非法操作!';1 p/ m5 c. \. Q, Q$ m1 k [/ U
- }2 h4 G( K* {* f
- }else{
# X7 f9 u% Z z+ ~" x - $data['msg']='还没有到活动时间,请晚点再来哟!!';# z9 Z! N8 Z, e( \7 D' i" d
- }2 G; C9 Y; V/ G
- }6 \) P7 E. ]: q: k
- exit(json_encode($data));
3 z# y& P& Z& j& F: G3 \% f - }
复制代码 : f: `1 M! c8 {
( E% L( L/ L l4 @ |