|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
) @" t; b( L2 m, A) B/ fMysql中的锁语法:( l3 f- W* g9 y. e) h. c) p, D
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】4 |# ]1 U1 Q" \& }
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
% G: ?: ?6 F% e0 f- A! GWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
( `4 E# d* v3 P, l4 J- }! k# p注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :% \$ g; I2 b% e& s4 h& G
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
' k$ k7 `/ C4 ~0 l2 w测试时,有个文件就行,叫什么名无所谓 总结:* y# H) Q8 _" \8 u, J& H
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
7 @ D! m9 p4 x8 `, V u1. 高并发下单时,减库存量时要加锁
0 \* ~7 }6 r2 m7 L/ {9 B K6 z( ^1 ^2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
) N6 n+ ?1 ?2 c% T - 模拟秒杀活动-- 商品100件* d9 `* Q. S! n" I( Y5 X, Y' r
- CREATE TABLE ta1 M2 V; p1 G6 c( S" G: q
- (
; Q& c3 c; Z$ a" Q# F& r8 ^6 C - id int comment '模拟100件活动商品的数量'! l& A5 `2 I& ]" W3 K, W
- );
$ q7 n( ?8 f, ^# D* c% g - INSERT INTO ta VALUES(100);% O' D1 _5 O, v0 t
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件. l0 T; j* p: b8 V* w7 y1 D* [
- */ ) V( Y4 ^9 ^8 v7 v# H
- 3 A; _$ w4 p) a" Q: {" p/ o
- // 关闭错误报告
2 v+ ^1 `" O. w9 T - error_reporting(0); 8 E" `2 a( M: k/ \6 j% n# k3 c, N
7 Z- r5 V: G; r3 r! k2 W$ E- $dbhost = 'localhost:3306'; // mysql服务器主机地址
. Y( d. |, o3 Z' n/ e - $dbuser = 'root'; // mysql用户名
7 f) y9 X, q% V8 L - $dbpass = 'root'; // mysql用户名密码 m/ j- l& Z4 _, Q' ~- ~. c
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);3 b+ H- B; O% f x
- if(! $conn )4 U8 j! R- K5 {/ d! o
- {& ^* X" x9 Y. }9 w$ D+ K- E$ h
- die('连接失败: ' . mysqli_error($conn));
: |* ]8 J+ R% l/ d( O+ b - }
- `' H3 j; i1 q' w/ i+ [, X - // 设置编码,防止中文乱码1 ]9 w9 N& ]8 m: o* i
- mysqli_query($conn , "set names utf8");
# s; {( `; R6 m" ^7 u# ? - mysqli_select_db( $conn, 'temp' );
/ p% K( C J) L2 W) z! ]: B
' D/ m7 u- x3 F6 a* p* V3 @/ b, B- g: J- # mysql 锁
M) m' w6 M! n. F+ u1 ]6 y* R - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 % {5 W0 |7 u4 J! ~* I
- $rs = mysqli_query($conn , 'SELECT id FROM a');
! f* E% L( l, [# v - $id = mysqli_result($rs, 0, 0); 0 p6 w% b+ t: c1 S: N% i, S
- if($id > 0) : ]/ M. R4 J: J0 u+ v- E) p* {+ k
- {
3 c; Y9 E( y+ I! z3 O x9 X% p6 T - --$id;
4 ?" T$ {5 T' h/ y+ m - mysqli_query($conn , 'UPDATE a SET id='.$id);
% T4 i; U! x- Z& W- ^! N+ c - } 3 F$ \7 U5 }" k7 W0 o
9 @( \, w! ~0 k( Z4 [! s! `- # mysql 解锁 ) q5 D3 M5 Z( A, L, ~
- mysqli_query($conn , 'UNLOCK TABLES');( s* C$ R( j5 O# R
- //查询解锁后的id值
: [, D/ M+ K9 t3 l) } - // $res = mysqli_query($conn , 'SELECT id FROM ta');
+ \4 X [. [5 F3 i - // while($row = mysqli_fetch_assoc($res))
" o2 \) I8 z6 Z0 Z - // {
) c) Y+ B# x) E1 y: W - // $id = $row['id'];3 Q- X z& E ~! o+ E( Q! |7 `
- // }
2 M4 ?7 N9 x: C7 G, T2 X0 q - // echo $id;
复制代码
$ m" ~" h& m4 o1 U2 X8 ?6 K, j1 A- f2 t
5 r" p9 S# _- r( O2 E) a8 gPHP文件锁示例: - /*; z" |) V% f8 Z$ X5 A9 ~
- 模拟秒杀活动-- 商品100件
$ O" T6 q, B7 N3 D - CREATE TABLE ta
, j+ q5 G1 O8 z* x8 s - (9 k0 A( V3 L. C9 |
- id int comment '模拟100件活动商品的数量'
/ l5 i% j \' C G9 E0 K - );1 C3 @ s& A/ X) R
- INSERT INTO ta VALUES(100);- L3 M, }9 \. z, m; i" D
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件 t# i$ \' m6 C0 o0 O
- */
, I$ v7 _, V: M0 |; @1 s+ F - ( T: w4 _% N$ ^( R& b( c. K9 k$ o
- // 关闭错误报告
! u1 H9 ? g2 [, o5 S- P - error_reporting(0);
# ?4 X; Z5 p% M7 e
) B% j8 B. K# F. l- $dbhost = 'localhost:3306'; // mysql服务器主机地址9 ]) D ^' t' Q+ f( g- n- x9 Y1 S) i
- $dbuser = 'root'; // mysql用户名
; e0 ]/ ` A5 _: ` l0 E* Z; S4 Q - $dbpass = 'root'; // mysql用户名密码' B. r$ s5 R" }7 O
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);$ T( N1 o* x9 R% X5 W$ w
- if(! $conn )
) k7 N' O+ b0 ^. h* k, w - {4 A% N& O' e, t: w& `$ e
- die('连接失败: ' . mysqli_error($conn));+ Q8 a1 C8 I8 a. q6 K: z
- }
. v `7 Q( T- p - // 设置编码,防止中文乱码
5 l1 M o$ o9 t - mysqli_query($conn , "set names utf8");8 ?* n, \; X, h% _4 T; r: `
- mysqli_select_db( $conn, 'temp' );
) }/ ?" h% m3 p- Q6 I9 F
, T2 r( w* K- E- # php中的文件锁 7 W9 e8 _8 Y% X' X7 P
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 - i+ K7 x! P' T$ s$ |
- flock($fp, LOCK_EX);// 排他锁
' J6 `( E8 l. ]1 a8 E2 A - - H6 k( `6 q* m4 J+ n8 i$ G
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 2 t* v" }$ X& p- S9 l
- while($row = mysqli_fetch_assoc($retval))
7 ]1 {9 W: P- o - {
% @9 y& p8 W( ^' P1 K6 t& \; }0 c - $id = $row['id'];% y' k8 k7 ?2 G% Z
- }
! Q( \9 X' T- E3 p: o# f -
3 | J$ F, ^9 A3 H - if($id > 0) 4 q: q1 E1 U y- H. @2 m
- {
2 q. ^6 o& }( I* ]. J, M - --$id; $ n |# I% E! ?' E/ X1 m+ _9 D) V5 d
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
7 B1 S% c7 N8 ~/ `$ M - } & I e# F a* v6 q9 l- [# D
- # php的文件锁,释放锁
$ a* d. k4 t5 L; U n1 v J8 p - flock($fp, LOCK_UN); 3 u* P( U8 p1 Q! G! D- }
- fclose($fp);) l/ C H7 v& h& z8 i e
- ' M+ {# u' Q" J5 e. A6 O
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
( m. d: u2 w* k4 R - // while($row = mysqli_fetch_assoc($res))
3 E; l' k9 n f- Z q7 ~1 x3 v - // {
; @ `( D' D8 y5 Z4 N# A - // $id = $row['id'];
* u* \8 X2 V6 p, h - // }" b8 o$ z1 G$ a9 p n& x& E" A
- // echo $id;
复制代码
* q4 [, X& f" Z) {; J9 A3 z
+ C7 {7 V# u/ c* T% \; ^8 r抢券活动实例: - public function envelopeSnatching(){
( [( V7 X, |( p2 j - $lingqu = $_POST['type'];+ P# b9 x) |6 y
- $uid=session('u_id');//用户id
( ?/ C9 d2 O9 X0 N; |8 B8 R - if(!$uid){: g# y7 Z7 k" u
- $data['msg']='您没登录,请先登录!';
* r4 T+ j# w. `0 o7 P/ j3 x! `; W - }else if(date('Y-m-d') != '2017-12-12'){
9 g9 ?# I1 K7 }( H1 Z' G - $data['msg']='不在活动时间内!';
. d% X+ f7 z) F# a% t8 v+ u! l - }else{6 i# d6 f/ _0 n' I+ x
- $hours=date('H');//当前小时数# Y1 k4 |3 |: X6 b0 O
- if($hours > '09' || $hours > '17'){
& S% z) c2 L8 L! x/ h% |, l
. [6 w m4 o3 A) v4 m' b- if($lingqu == 1 || $lingqu ==2){//点击10点的
8 n( J1 m2 s( n( F6 m5 X: b/ M% L& d - if($lingqu == 1){
* w5 a, q5 {4 @3 i# A5 f+ O m& o - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过- E+ v/ O2 e3 v
- if($hours > '09'){5 `4 w. R8 S+ `) t+ [
- $num=mt_rand(25,28);//优惠券金额
* _: k# ^# w" @) H. I - $id=1;
2 A& L! H6 i, P; X/ [! H. P8 L - }" R( K9 R4 d6 |- e
- }else if($lingqu == 2){5 c- }2 a* R% o" A) o- u
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
5 X& Z+ s. F) O - if($hours > '17'){/ V( K# O, ]+ U4 `' Y- ^
- $num=mt_rand(50,55);//优惠券金额
) C1 Q9 @/ _2 L" v' ?) o - $id=2;
5 D' B9 i0 l$ p; B1 m - }* C4 F0 e! G* h
- }7 d7 J2 y1 a# L, q) z, Q
- if(!$id){
! [: b( i( }% M( U3 N! R) o - $data['msg']='时间还没到,晚点再来吧。';
* w- H- _/ a4 i2 C' r+ e - }else{% O) l r$ f+ O8 p% e9 B6 K
- if($is_lingqu){2 c! c& E+ ^; ]1 X, d( g/ C
- $data['msg']='你已经领取过了,留点给别人吧!';
3 m% J9 R* ~0 b! i7 ~; A7 d4 Z$ ` - }else{
- J3 d# t1 J$ h& P$ X - //锁表
& K' \/ s0 Q) @: \1 {, r - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');6 B# u- [5 v, k B; \
- $active=M('active_num')->where(array('id'=>$id))->find();
% y/ `. _1 v! P, x1 O+ Y! V/ J9 [9 W - if($active > 0){1 Y* o, w& t" T; d$ W2 B! S# Z/ C+ X
- //开启事务
d$ J$ K3 N( S. s - M()->execute('start transaction');
: e- b; j' a. G - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));4 D: h( J; c7 P: y
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
' J' h+ A. x5 P3 f9 `4 e' N - $members_preferential = M('members_preferential');
" r' ^ I+ p9 V - //对应投资金额,% A& e* H* P6 f# V
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));6 h% b' C: s+ X0 _! E H9 n
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');+ J+ P2 [- c8 ^% n/ p3 q
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
: r! `) z; A& [) j) M - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
- l% T7 @9 K+ n9 U _ - if($save && $add && $add2){% W2 O1 ^. A( W+ \9 x/ F& \0 j+ T
- //事务提交
9 Q' W& R- c) I0 v - M()->execute('commit');; T' o, P% `+ o. V3 Y' ?6 @+ c
- $data['msg']='恭喜你领取了'.$num.'元优惠券'; q! H% z% e7 o+ Y) N/ V4 P. \
- }else{8 M% ?5 }, t/ k. ]5 c' T! X7 j( g
- //回滚
1 l: U }. E8 }# R+ z7 Y - M()->execute('rollback');+ P0 f# f1 H A. j
- $data['msg']='未知错误!';
5 I$ c& n+ a" |+ t8 i1 S - }; Y# o; K0 z6 M, T+ ~, T
- }else{: `( @8 z9 B( c! I4 P
- $data['msg']='红包已领完,你来晚了!';
* C8 v* N( S1 u O8 j - }. J1 j r- b6 h, E; ~
- M()->execute('UNLOCK TABLES');
9 o$ h3 r. ]) o- h5 g1 K' T B - }
! z$ u" |/ R" W4 | | - }
8 @, I9 g" T: E* C - }else{, H( x- v! K, ]5 m/ H1 [8 ~
- $data['msg']='非法操作!';
: j5 j, N2 L, k - }0 K5 I# I& T/ x9 q, T
- }else{
5 m3 D6 Y$ [0 |% U' x - $data['msg']='还没有到活动时间,请晚点再来哟!!';
( }" s' y- b' ~% Y. ] - }5 d) s$ \! O# B' A
- }& V' C+ C/ d# g" D# h; P
- exit(json_encode($data));
# S0 v' ^9 h! U. z - }
复制代码 . ]5 G! o; r. y
: E8 p; W$ N7 }
|