|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 . Y7 j' W- G" ]' r2 ~
Mysql中的锁语法:
% D+ |: z* K) g* N6 KLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
# H, d- q9 k+ |* w! K" ?4 i! AUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表8 P* n; N v* g
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
n" X5 z8 p- ]- K2 Z/ O% |注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :2 M% c# e5 j: z7 \" a/ {) X8 `; \
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
( ?/ E6 L4 ~! F" B测试时,有个文件就行,叫什么名无所谓 总结:) V1 E/ c; ^9 c) V, d+ L
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
0 }% U" i) A) v- G$ U# j) X1. 高并发下单时,减库存量时要加锁
4 G8 c; [8 U$ D, v' |' S2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*$ P& ^9 V" j* n B3 ]/ v
- 模拟秒杀活动-- 商品100件
( J5 F% a5 w* _" t" a8 i, A- V - CREATE TABLE ta( M; E5 Z0 z+ s) ?% J' |6 P
- (& m2 _7 `' T; E" t, M2 I
- id int comment '模拟100件活动商品的数量'
% E) v# g# S2 C - );3 i1 M) n n8 k1 d, S7 r
- INSERT INTO ta VALUES(100);/ m6 g7 |: h5 O: U5 l
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
1 ~! y* X( d* I - */ # |3 q3 v: m4 X5 k6 L. n
-
& t5 Y8 w; z& ?# M - // 关闭错误报告
, |6 l" b4 J0 ~ - error_reporting(0); 3 h4 O# C5 a# |7 a% r1 I
! v( ~8 I, O9 s4 K- Q- $dbhost = 'localhost:3306'; // mysql服务器主机地址) Y8 G7 O$ {* A4 B( m! @; p. _
- $dbuser = 'root'; // mysql用户名
* e; U% y% Q, b. q @ - $dbpass = 'root'; // mysql用户名密码& T* ~" d; H! R, Q- C
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);3 w+ W+ r0 t5 Y; ]# ~* g
- if(! $conn )) M7 Z6 f- x0 Y$ G1 B8 E. U
- {/ p% p; H3 b1 P4 b
- die('连接失败: ' . mysqli_error($conn));
4 X L) t$ e! \) g, |: z/ } - }
# J: W& P, j" t - // 设置编码,防止中文乱码
; j" ~* N8 P7 o- L$ e - mysqli_query($conn , "set names utf8");
* P& C- u* [. T, n - mysqli_select_db( $conn, 'temp' ); . x/ K4 j& R- K* p, |
- + X R6 y$ m. J. o( G' [3 I
- # mysql 锁
# M( s! Q5 t9 W; l$ }: o; a - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
2 r! _3 ^8 x' Z - $rs = mysqli_query($conn , 'SELECT id FROM a'); : p9 ?: O' {7 L0 a+ y5 g; J
- $id = mysqli_result($rs, 0, 0); 2 f$ q4 Y1 Z. K) z) q
- if($id > 0)
2 M/ u! o# T/ k4 E) V { l( p' h - {
3 x6 q/ |# u6 g: c# e" Q5 C: w& o3 K. s - --$id;
; [. Z5 s! n. y, b# w - mysqli_query($conn , 'UPDATE a SET id='.$id);
V& B4 v5 T2 r9 a# q' T - }
$ S1 |; c0 V) D) f4 X - 8 Q: H; `7 H" ?9 S
- # mysql 解锁
+ i! R; M" ?* W0 \* y" |2 J) ~ - mysqli_query($conn , 'UNLOCK TABLES');5 I$ P- s* e2 V8 e( O* H1 E
- //查询解锁后的id值& G: G; I0 Z% C/ j
- // $res = mysqli_query($conn , 'SELECT id FROM ta');/ J8 k' V7 H4 W* k) x$ v* _' I
- // while($row = mysqli_fetch_assoc($res))
( g2 w: f! d! Z7 B - // {
3 v" M. H' T* W: Z7 p3 n5 J - // $id = $row['id'];
, c8 D. b, E$ y5 E0 ` - // }. ]; b" Y# Z# n! i# S: n4 c
- // echo $id;
复制代码 ( d+ s* I3 M9 W, x4 C2 T! C
) f6 v: O: w% ^% Y0 A- f* L6 {) r
PHP文件锁示例: - /*
, l4 ]; r2 V5 _6 z6 y/ l* i - 模拟秒杀活动-- 商品100件( M7 |+ G2 v) ]8 ^9 [- V
- CREATE TABLE ta7 [7 q% a( c/ `3 b% |2 }
- (
$ T+ Z' l/ r' y - id int comment '模拟100件活动商品的数量'
" ?4 T0 w. o) m( E - );
1 W5 q. z( y. K+ `5 Z - INSERT INTO ta VALUES(100);
4 w7 m. N( S3 O- h' @. _" x - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
: K5 T \5 ?4 f) S& s - */
/ v! J3 p% u$ ^" O) D6 o1 f, p - + l- M" ^7 Z+ W8 f, X/ [
- // 关闭错误报告2 m/ T$ b% J! a5 ]
- error_reporting(0);
4 N) b& `9 y, M3 `' |4 y! s
( {7 ~% F1 |4 d- $dbhost = 'localhost:3306'; // mysql服务器主机地址 Y2 B: Q2 }1 p% q# b
- $dbuser = 'root'; // mysql用户名
* i/ T0 v4 X% ]1 [2 u) x& v3 Y! Y - $dbpass = 'root'; // mysql用户名密码0 v7 T" T1 P4 Y( W+ ^1 i
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
6 F* L: S. B. l( S - if(! $conn )& u- y4 x' p: O
- {
: H- F, I& h* j' A3 }8 t - die('连接失败: ' . mysqli_error($conn));/ H3 K8 w! S2 S4 f0 c
- }
y+ S. a# K, ^: C& r/ `) A - // 设置编码,防止中文乱码# F2 r! s. H; L: k) |% @
- mysqli_query($conn , "set names utf8");/ d# N9 @6 ~$ G
- mysqli_select_db( $conn, 'temp' );
3 _8 m9 T3 q6 ?# q
: |* A* q1 D5 s' S+ M8 Z# J: c& z- # php中的文件锁
: X" V2 F1 l3 E r' D - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
; L: Z5 @! E" ~/ G - flock($fp, LOCK_EX);// 排他锁 & s- u7 u, e& H5 ]0 ^9 U
- 5 v3 G, ~9 u# H+ A
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); . U" e% ~0 n- b \* G# f3 u0 Y, u
- while($row = mysqli_fetch_assoc($retval))3 T+ O+ E* c( |9 A; k4 J
- {
" w! m4 E1 r5 }# g - $id = $row['id'];7 o; m$ K4 F! O" u# M
- }$ L) E: B) u( [7 |8 f" l1 p4 ?! p
- ( C( g1 D! ^% L+ ~- S5 R. r
- if($id > 0) h s6 R4 s- p6 O- v' m
- {
% ^" c( c0 C! Y* p - --$id; + _! P0 s7 `7 `0 V8 z) \) \
- mysqli_query($conn ,'UPDATE ta SET id='.$id); ! l- F* I; M" G; r* o$ i& N2 b
- } 0 G7 }$ F+ `/ x+ z2 A/ l9 h0 E
- # php的文件锁,释放锁
( u2 F! q' m/ G - flock($fp, LOCK_UN); ' ?/ X, M2 y4 o6 n+ h
- fclose($fp);
$ ~# Z* T* |( v) q - ) m4 s# f% I7 V3 Y+ C" n J
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
& r& `8 b: v5 ^; J# Y; O - // while($row = mysqli_fetch_assoc($res))
4 u8 Q* ?2 p h9 s - // {
+ P# `6 K. e: C - // $id = $row['id'];
1 r7 \7 J; N* a: A* u3 y4 C4 Y - // } W( D. c" M+ {& @3 f
- // echo $id;
复制代码
2 p2 ]& e& I# I/ {- M9 a F1 h& w: @+ Q. C1 Z
抢券活动实例: - public function envelopeSnatching(){7 |4 O2 Z4 p% O! m9 U
- $lingqu = $_POST['type'];
% ~1 T6 M9 D/ g* ^8 M - $uid=session('u_id');//用户id
! Q. F# h g& |7 L8 n6 I - if(!$uid){
9 S, d. P/ d& c! q/ u5 F - $data['msg']='您没登录,请先登录!';9 Y, ^7 u4 ]! Z0 |
- }else if(date('Y-m-d') != '2017-12-12'){
, o* B" @! X3 Q. ]6 j$ y% U- ?& e - $data['msg']='不在活动时间内!';& K, i. B6 \9 i3 R5 O6 S( q
- }else{. Z0 R) s; z- W. v/ U1 G
- $hours=date('H');//当前小时数
5 I' {# R' Z1 G; M - if($hours > '09' || $hours > '17'){- p; M$ S2 }, W d6 P% S
( r7 T1 d( v( b9 }) @- if($lingqu == 1 || $lingqu ==2){//点击10点的
1 t! p8 @( e5 S8 P6 D - if($lingqu == 1){0 g5 \- x( P# e* [8 E& R$ X/ Z
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过+ F3 b4 T0 E5 Y* v; B
- if($hours > '09'){5 [. j' V5 D% o! @8 ^
- $num=mt_rand(25,28);//优惠券金额7 n+ p, n8 r4 C3 V
- $id=1;9 l( ~1 t0 ]3 U% A; m/ I7 W: v
- }& m" K: x+ I D8 g3 a) W
- }else if($lingqu == 2){2 |, f. j6 {; ?. G
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
% S# ]+ v. ]- y9 ?3 l# E; a; s- h - if($hours > '17'){" G1 n) h& P( X2 J0 H* V2 B
- $num=mt_rand(50,55);//优惠券金额
: C9 Q" |# p& i' n# @. H - $id=2;
' l% d7 s% |4 v/ n) _- o- e% G( B: B0 W - }
3 E1 C) Z& I1 D$ F - }
+ u2 m, _. |5 r& Q7 m: ?7 n3 o' L/ z - if(!$id){
! z- O' E- z8 V1 z) { - $data['msg']='时间还没到,晚点再来吧。';
/ f5 N8 f' K9 s# w) R - }else{: q, ^/ E! `4 F, T
- if($is_lingqu){* ?/ n7 A. @" G+ x, G
- $data['msg']='你已经领取过了,留点给别人吧!';5 D, n S# a5 }; N. X% \6 |1 y
- }else{" w' a9 m; ^: P, T6 D
- //锁表
: w, Q, |! S5 f4 e% m( M; D* x - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
9 p0 I0 q& P, A/ G - $active=M('active_num')->where(array('id'=>$id))->find();
4 ?6 {7 S" Z& \" y - if($active > 0){' R7 p2 F$ d6 i
- //开启事务
1 p. n* S- l% A1 a4 b4 d$ Q - M()->execute('start transaction');
! E$ [1 v9 V# Q* [/ d - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));5 t8 u) g9 _, h& n9 U8 S- O, R
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
0 H% D; a* a5 z- T - $members_preferential = M('members_preferential');
$ K+ ?2 M8 N3 R |9 @* c# C6 Z Y - //对应投资金额,* _5 m9 K3 C2 O7 K
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));8 [# @& o+ R. D0 \1 J
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
[. C! Y: {4 {" ^% E1 D - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
3 K& Z; x) M( m) a' Z( u b - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券. ?3 r7 p" `7 g" o' z+ l- K5 x. v& W
- if($save && $add && $add2){
3 X, Z5 j) b# w/ m& X - //事务提交! B: U9 d5 k) m3 @
- M()->execute('commit');
, L7 |2 c! ~& l# F& Q - $data['msg']='恭喜你领取了'.$num.'元优惠券';
% N6 J( w; t4 D: N - }else{
% H, t3 W& |7 Z1 Q/ s* I, I - //回滚( R6 M: s2 u4 f$ K6 X2 H+ q
- M()->execute('rollback');
; Y9 d G5 h C, t) v - $data['msg']='未知错误!';
+ Q& C6 q' J8 b' z+ w9 U, f - }: U3 A) i/ P0 G# }! w
- }else{
% H7 N9 R) X/ w J" t - $data['msg']='红包已领完,你来晚了!';1 K# ?9 P/ ~8 ?; G" A# S( e' F
- }' P! v$ ~6 o G" j/ N4 n& F" N/ O
- M()->execute('UNLOCK TABLES');. L/ s& j/ X/ P5 b+ P
- }
6 N( j$ ^) @' \ - }
# `1 j8 D( B8 E- u0 q% n# N" \ z - }else{) S6 I+ x- b& ?- k# m
- $data['msg']='非法操作!';/ ~1 t4 U- Y' W" D0 n+ h
- }; z7 y' a( C) S6 o
- }else{: {2 S% D/ ?1 r9 r! C! U3 x( M
- $data['msg']='还没有到活动时间,请晚点再来哟!!';" ]) O, f9 j) G, u. k
- }
- b. `' T; X+ @1 `, Z( t - }4 A8 V4 I0 m/ W, S8 ^
- exit(json_encode($data));% S% I2 w7 G6 F: {% @
- }
复制代码
9 G& h5 c2 ^5 H; K* W- Z
7 F( p/ ^ p1 f/ L& s1 N |