|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
" P# N8 M' K9 s4 } l" KMysql中的锁语法:
- G7 X& u8 t; u# S$ Y3 I5 ZLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
6 ]2 d% W7 Z1 ^- a6 LUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表0 w6 z) i2 \% e* k" o. Q
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
7 u% E J8 a9 Z7 _" L2 A6 `. a注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
0 a+ x; U$ i) S- q% A- `文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
+ J+ J& J- y8 }/ g7 p+ V测试时,有个文件就行,叫什么名无所谓 总结:) t+ ~- E0 n! t7 A2 u
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
8 H( S5 `& a. L! m8 `5 E. h2 ~1. 高并发下单时,减库存量时要加锁
2 l* y1 p5 J: y4 [+ n# L+ P/ j1 _2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*1 _' L# K: \5 ]5 T# w: R; c
- 模拟秒杀活动-- 商品100件
" @& D4 Z$ Y a* p - CREATE TABLE ta W7 Y0 E2 A7 D; |
- (
- z6 U- ~, N ~7 G8 D8 ]& J3 v! s - id int comment '模拟100件活动商品的数量') r1 Q2 W, e, _" {% V) x
- );$ l* t3 i# T$ e- f8 j3 K
- INSERT INTO ta VALUES(100);
+ a5 ~0 ^; T4 o - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
" ^: V0 k6 }/ [* S' a - */ 2 q+ s5 k1 c" a6 E. _
- # M0 l0 A1 q/ A; C% a. j/ R) l9 ^
- // 关闭错误报告
* |0 f% I \; [" d$ P - error_reporting(0); y! o" P7 F9 |3 I/ }
+ V* u* b- W$ A9 F- $dbhost = 'localhost:3306'; // mysql服务器主机地址
. p# S- \7 D: M) r, s% _+ ? - $dbuser = 'root'; // mysql用户名
% S: z* V- r( }3 \ - $dbpass = 'root'; // mysql用户名密码
: x& g G" {$ [, W - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);5 L# C8 r+ K4 _$ w
- if(! $conn )6 g4 o1 v6 F1 D% {* P+ f3 f
- {/ h: |& V0 y' B
- die('连接失败: ' . mysqli_error($conn)); g/ `- E* ~8 o4 k4 u- a- d3 ?
- }
( Q9 P1 z. L: }: B8 @$ I+ m- Q ` - // 设置编码,防止中文乱码& k6 N8 ~( c5 e* j8 P, @
- mysqli_query($conn , "set names utf8");
" b+ m! \- I: q9 t- k) Y - mysqli_select_db( $conn, 'temp' );
/ J# M6 i" s: n4 I2 ?9 R/ ?
" i6 S7 [; r9 Y" R1 L- # mysql 锁
9 p! [9 h0 @3 N8 _$ M - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 4 `* u& M. k) ]9 a
- $rs = mysqli_query($conn , 'SELECT id FROM a'); 5 C, O- E0 u% H z* @: U
- $id = mysqli_result($rs, 0, 0); / g3 l4 Y" v7 W9 f7 b+ E
- if($id > 0)
0 P1 r9 l! E7 {' B" T" w - { 5 x! M8 j6 w" t4 ^+ E
- --$id; , m' r- V# l$ E
- mysqli_query($conn , 'UPDATE a SET id='.$id); 9 _+ j. T3 V. D4 R/ [/ Z
- }
; ~" ]. h$ t, H, Q$ e
P6 q; D% _& o2 r* h6 C! H" d \, l- # mysql 解锁 4 V+ y- b8 H) T. H% R) ?9 m3 l8 v# n6 I) i
- mysqli_query($conn , 'UNLOCK TABLES');
; V4 E8 _ c5 k& p - //查询解锁后的id值
, N3 k( r9 `: M! _/ \ - // $res = mysqli_query($conn , 'SELECT id FROM ta');
* |" q' B: d/ Q% C( s# e& \ - // while($row = mysqli_fetch_assoc($res))$ B3 O4 Y" S" w$ i! f
- // {+ r2 J3 S* R( S5 C# E
- // $id = $row['id'];# b/ b# u# @) \8 }0 Q* H7 C
- // }
. N( k* E h# W/ g( D- V- e: n - // echo $id;
复制代码 ) ^* t5 X# T8 \8 c- m! G; ~! L: n
: `- q4 `1 K" s# N+ h+ `
6 e2 T; l7 a5 w& V1 b! N% @
PHP文件锁示例: - /*4 E& L) }/ i6 t0 _6 c+ f _8 v# t% m; t
- 模拟秒杀活动-- 商品100件3 S- }" W: S! |
- CREATE TABLE ta
0 f5 `. y/ o" J - (8 V, v2 Z @ g. Q. J$ }9 K! @2 o
- id int comment '模拟100件活动商品的数量'* z; |6 R$ w6 t$ B y7 A {
- ); g# ~; k$ X4 m" }8 Y
- INSERT INTO ta VALUES(100);
2 n5 R& V( X! k% h& |- f- a. i - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件8 W$ J- u& J' b
- */
9 \6 {& K; e6 t- ^' ] - $ D$ F' W. v& C4 O0 K
- // 关闭错误报告
8 f5 }; q4 H' Y2 `. E- z - error_reporting(0);
/ \1 L* S. _8 o3 z3 s- C- O
$ s( H, A6 Z: |# ~: G; K" R- $dbhost = 'localhost:3306'; // mysql服务器主机地址
% ~5 K. x! t- S5 F8 Q - $dbuser = 'root'; // mysql用户名
; y' |' j9 H/ Y+ [' L - $dbpass = 'root'; // mysql用户名密码
; N2 U, N& ~+ p - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
0 R: q/ d/ a( Z: b9 p - if(! $conn )4 S3 A. B0 Z5 P/ ^
- {
* f [) L! h0 ~( t$ U - die('连接失败: ' . mysqli_error($conn));/ X9 Q6 o- X2 c/ [7 W6 j- ]7 f& o
- }' j3 R+ ]7 x; @' m
- // 设置编码,防止中文乱码
# S C6 t- a; ? - mysqli_query($conn , "set names utf8");
+ u7 g9 G6 [4 y$ x0 H - mysqli_select_db( $conn, 'temp' ); ' U5 I2 D) b+ v L' p5 t
+ f F( O w9 g' t5 L+ g6 } N- # php中的文件锁 7 D/ _* l; u% \; m2 ]+ u* g
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
& F/ f6 R" b' X- d, R/ u- ^ - flock($fp, LOCK_EX);// 排他锁
2 ?# |% L! x( g- g - m; E3 K8 @5 ?% _. M
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); ( j f( b: w5 [0 { y. j
- while($row = mysqli_fetch_assoc($retval))
( i! `5 a) n% z6 g - {
' V2 c( X: z$ z( s \: T' v* D2 J, ^# a - $id = $row['id'];5 @/ z; S+ O+ t; x( h1 A" c# N
- }
4 Q n1 G7 _8 R$ C - % a$ I- Z' B/ E& f5 Z& p
- if($id > 0) ( u: Q$ W; S6 z' Q7 o {
- { : N' l w+ V; D
- --$id;
6 d4 M( t5 l# v6 | - mysqli_query($conn ,'UPDATE ta SET id='.$id);
# j3 A& V9 @3 P+ ?8 u3 l - }
$ e& L* v. s) | - # php的文件锁,释放锁 1 E) g; n& ?4 F5 w1 @! a% k- I/ ~* d
- flock($fp, LOCK_UN); . z- E# G; M* e
- fclose($fp);* C8 Q* i# _: j; d7 k
- 6 q% L% |( R- i
- // $res = mysqli_query($conn , 'SELECT id FROM ta');' `+ ~5 d+ e7 l. X! y/ P" K
- // while($row = mysqli_fetch_assoc($res))7 \5 n3 O4 \# z
- // {9 f8 A) n$ R# w6 O+ T
- // $id = $row['id'];
$ ?# A: a: ]! l, i2 C - // }' k, U+ ~0 P5 E) Z# n
- // echo $id;
复制代码
4 Y5 \: C( e' T n3 Q6 f# u7 {5 U/ U, n0 q5 w N& @4 @3 G
抢券活动实例: - public function envelopeSnatching(){9 J3 v0 j" T! Y: d3 _2 r5 @
- $lingqu = $_POST['type'];0 U# K* O! d6 j9 y, }
- $uid=session('u_id');//用户id$ m# C% X+ f7 i) o
- if(!$uid){8 U4 |5 P7 P- `
- $data['msg']='您没登录,请先登录!';4 {# m& L9 o2 ]' @0 R0 G3 Z R6 ^
- }else if(date('Y-m-d') != '2017-12-12'){
9 J- g! n. `# v" V1 U' |% _- T/ m - $data['msg']='不在活动时间内!';2 [$ S/ r6 g3 o4 z8 c3 `1 z
- }else{+ e' A' x: Q. i0 G; H. |
- $hours=date('H');//当前小时数; k1 [; m- @( M( y9 P
- if($hours > '09' || $hours > '17'){
. ]2 n/ ?( r, Z. x: {# |/ i
7 h7 E, l `" v6 o- if($lingqu == 1 || $lingqu ==2){//点击10点的
- ?1 G e3 b2 C j5 e8 X+ O - if($lingqu == 1){9 t0 J- Q5 ~6 N
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过3 f- o3 K9 r0 x2 B* ]
- if($hours > '09'){# S7 o: u& {$ d6 f
- $num=mt_rand(25,28);//优惠券金额
7 K$ E$ m* O; J+ z, v& y. W - $id=1;3 R# z0 P- g' u [
- }
8 g5 o' ?! I0 } | l" J8 q& f- x - }else if($lingqu == 2){' y! ]! I/ Y; @( t4 d
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();, d0 p1 |. Y/ G6 @0 w
- if($hours > '17'){* N a8 w' j; w) X3 v4 P/ Z8 `( }2 W$ K
- $num=mt_rand(50,55);//优惠券金额
" }* Z3 `" k+ @/ ?" h4 j - $id=2;- Z& A" o8 Q/ v0 x- r8 E: m
- }: I9 j9 c7 D: V0 f& y. |1 @0 Q/ l+ s
- }
' K+ i7 V$ _* e - if(!$id){4 \; {# _: N$ O
- $data['msg']='时间还没到,晚点再来吧。';
3 _8 p0 x3 D/ e - }else{" _. |' P( K5 ~3 I
- if($is_lingqu){
( ]& [; J6 b" j, k - $data['msg']='你已经领取过了,留点给别人吧!';8 }+ S9 v$ h3 I, S( N0 o" u
- }else{
2 s' E" U% j9 T3 U - //锁表
|7 T0 I" X' Q" J/ i: M9 x2 Q* i - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
K& [9 v* C/ L4 m3 \. E - $active=M('active_num')->where(array('id'=>$id))->find();
5 ~1 l6 C. a. W8 m - if($active > 0){, W! r7 e* D" ~( ~/ v
- //开启事务0 K& G8 T. ~* k0 `1 s5 H
- M()->execute('start transaction');
& P3 q! p! z I- t( X7 p1 ~ - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));7 g3 w% f5 v0 N2 H
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
/ i' J/ b3 Y/ o, u: E; l - $members_preferential = M('members_preferential');
) v2 o8 c: T, b1 ~* D - //对应投资金额,% j9 q; } u" X; {* w# N
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));- R) Q0 U6 r; F% W5 L8 n( w, o9 b
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
/ i8 I& M! Z) b0 f7 J - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间9 H" _# ^7 I3 g8 | g8 j
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券' C" @7 p* X& O
- if($save && $add && $add2){
. ~1 @$ Z+ O1 m - //事务提交
1 l1 F. {4 q9 D! m. q8 p& m - M()->execute('commit');
' K! O4 S: T- d, c - $data['msg']='恭喜你领取了'.$num.'元优惠券';$ y4 l9 A( p8 {/ n1 K! R2 G
- }else{
% Q, g& x* v3 h; p; z2 r6 q - //回滚
- w! x; y- a* _5 x# ~ - M()->execute('rollback');
+ S; f- \7 c6 z7 {# d- t, @: D) m - $data['msg']='未知错误!';
& P% G; U! K6 q/ J+ J - }
$ n4 k6 c8 @9 w/ Z i1 R7 ]! P. H! G) g - }else{: j4 z& L0 a I8 {4 d
- $data['msg']='红包已领完,你来晚了!';
: j$ G4 i! T( o# S% `' b7 f - }# _' o+ S! h: f4 p, I/ k
- M()->execute('UNLOCK TABLES');
2 R8 Q0 R7 `$ w8 e4 i - } \9 o& b# W: P+ g; F$ H5 g/ B- v* Z
- }9 Y, e2 P- B u% Y0 l
- }else{
; c, |; S. m* R ~6 D h - $data['msg']='非法操作!';' f: Q, g# l6 \7 g
- }
, E% }( v5 z% ~6 ^ - }else{+ |% c2 R! n4 @2 i* E4 L3 ]- }
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
- {) s2 q2 O$ k, D3 X* C6 `6 Y$ M$ L - }1 x/ K: a! t& L# G* V
- }$ c) }; h/ [! ?7 Q
- exit(json_encode($data));
2 A x% { |0 u7 G7 q - }
复制代码 : v/ i8 c4 X+ R
( E) }$ U4 w5 a5 n6 |4 p
|