模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 $ i2 v* E/ c: W% X q! H2 h
Mysql中的锁语法:+ O6 A9 N" s- d
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
, P1 h+ \! X/ Z2 V0 UUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
, X6 Q% r* Q; z0 T2 s! e7 ?5 r$ eWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞& ~/ V' e" h1 [
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :% B4 g8 F) s, Q9 a+ `6 | f
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
; W8 L; m3 j) T$ x8 w测试时,有个文件就行,叫什么名无所谓 总结:
" x' p* z% Q* P R项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
0 R# y& d# X& w6 z9 u6 [1. 高并发下单时,减库存量时要加锁; U! K: g- r5 Q- M, y1 s8 P: R
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*$ v7 h: Y& H" Z$ [
- 模拟秒杀活动-- 商品100件
/ l' s1 d" [9 s& k G* f5 F7 f: ] - CREATE TABLE ta
8 q7 i# l3 O; ^ - (' A+ q5 n5 Y7 E# S7 ^
- id int comment '模拟100件活动商品的数量'* x5 I; Q v& L& g) ?3 n0 R
- );
2 y) M0 n" p8 U1 O' U. M - INSERT INTO ta VALUES(100);) I4 d2 g) b/ E Y
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件( Q' V# e! {3 t8 a) j
- */
4 T* a2 u: O; X# Q u -
9 \6 A- o, O. m7 H/ Q" h! ^ - // 关闭错误报告& H+ x* Q0 N, }7 h B
- error_reporting(0);
0 B+ b: P7 H8 h6 g" { - & j& _! q) a" }
- $dbhost = 'localhost:3306'; // mysql服务器主机地址9 s/ O+ N1 w% w, e
- $dbuser = 'root'; // mysql用户名3 r) _9 i/ }' L. K
- $dbpass = 'root'; // mysql用户名密码$ [8 _* V! s. ?5 k5 E
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
- h/ @& h/ e5 z - if(! $conn )8 x" V& l9 L A- G- X
- {1 D" D& S5 \4 l6 _! U, C2 I
- die('连接失败: ' . mysqli_error($conn));
, W: a- A& O8 c9 g - }
+ A0 r/ [5 R1 K& e2 x9 y8 z - // 设置编码,防止中文乱码
8 {0 \' F/ C& n9 M T8 j W - mysqli_query($conn , "set names utf8");
* r, u/ E) j e8 T& S! ~( u8 u/ } - mysqli_select_db( $conn, 'temp' );
/ G. X) B( _# n( K& v/ { - 0 f+ k' [( N9 _- o4 Q# {0 w+ T
- # mysql 锁
1 Q0 `; `+ V& w# v. z) u - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 & N$ ?) o# g! X8 m# y8 [) e
- $rs = mysqli_query($conn , 'SELECT id FROM a'); ) j6 `4 x5 Q/ \" o8 @9 K
- $id = mysqli_result($rs, 0, 0); & n8 Q/ P; B- H
- if($id > 0) f* y( y e' o! m& t& g" ~: u
- { 9 J. g. Z' h( S: r
- --$id; ; u8 { p0 z) X1 ]. P8 A, R
- mysqli_query($conn , 'UPDATE a SET id='.$id);
1 i( }6 u- d( r! e4 j* H - }
3 a+ O4 R$ p2 k$ l0 O+ _; V
" W/ G0 T+ Y' h0 E7 W" N- # mysql 解锁
8 ]/ v$ L. i7 E1 h' B) I, U - mysqli_query($conn , 'UNLOCK TABLES');
; i, \3 ~8 W! L/ D" t: n3 T - //查询解锁后的id值
% y4 X$ Z; N) c( _( E - // $res = mysqli_query($conn , 'SELECT id FROM ta');
1 ], c4 S. E) P* a6 F - // while($row = mysqli_fetch_assoc($res))
# M1 S. J! P5 x5 [: i - // {% J9 _4 K1 c& I8 D& ^" `: G3 ?. E' d
- // $id = $row['id'];
5 L: I* e0 f. |0 Q2 V/ W* K - // }
/ W" x4 u" C4 l - // echo $id;
复制代码 / z9 K9 M# O1 \. \! g9 c
& D, r6 j5 R0 U5 m
9 B3 _! f! E8 Y' K: V4 z% v0 V
PHP文件锁示例: - /*9 L: X) B8 X% |- b- M2 O: P
- 模拟秒杀活动-- 商品100件, m. c" p9 A, J& ]' y E
- CREATE TABLE ta) W/ s( N0 X3 Q5 n9 B8 f- g
- (
* W- H0 U# E1 }2 S$ U - id int comment '模拟100件活动商品的数量'$ L* x M$ G E6 B
- );) u- P( a A' p1 c8 }
- INSERT INTO ta VALUES(100); X' ^! @% d7 j3 a2 Q
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件3 M& L( o) l8 J2 l
- */
3 }- ?8 K% y5 B1 f" y7 g1 A0 g9 r9 H9 O - & [9 T! R* i" O2 x$ |
- // 关闭错误报告! S9 i; x2 a! ^* q! Z$ F
- error_reporting(0); & I+ k" ?( B( s2 O7 f
- f% {1 P9 h4 B+ l; Y: X" y) [3 V( c
- $dbhost = 'localhost:3306'; // mysql服务器主机地址6 P+ J/ Z# d; b3 M; Z6 v0 @. U
- $dbuser = 'root'; // mysql用户名
+ j' J2 ]0 Q9 F$ g) y4 t - $dbpass = 'root'; // mysql用户名密码
/ k" G: A+ q' |3 h - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
4 ]& B6 Z. X7 n7 x: I3 }% z" z - if(! $conn ): H* K) q N% k4 F2 K
- {
! ^ N' t1 J( C - die('连接失败: ' . mysqli_error($conn));: q; C' V1 |$ e9 a0 H# h. @. s
- }
& h& O1 r. b$ N- R; r, g - // 设置编码,防止中文乱码# E; T4 B+ `# J6 W* x/ J) j8 ^
- mysqli_query($conn , "set names utf8");
/ y4 T9 s3 g* d( ?7 v g - mysqli_select_db( $conn, 'temp' ); & m/ I; ?+ b# G r2 Y
- 2 E7 G5 ]- U. K5 J, R
- # php中的文件锁
3 q5 }* D1 {2 E/ o& @/ P - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
$ Q' B. k- [8 _- k" T - flock($fp, LOCK_EX);// 排他锁
5 g* Y* @7 q% k7 Y Q* x
' `( S6 |% C$ E- $retval = mysqli_query($conn ,'SELECT id FROM ta');
! d! w. Y5 B" b* a2 }5 g1 | - while($row = mysqli_fetch_assoc($retval))2 V) ]1 l0 \, U6 {7 v9 P
- {
1 t3 B3 \% S' f( B0 o$ i# s! a h - $id = $row['id'];" |- J& S5 [9 ~9 V. t/ }1 X9 |" M
- }
# p' j% g8 M. W# g. p* k -
5 c! V |7 H1 Z" \# i/ U. n - if($id > 0)
, U! P+ i2 c M, B2 R2 ]+ v - { ) n: Q5 X% ?. f' v- I
- --$id;
, R f6 J6 }; W. t# a - mysqli_query($conn ,'UPDATE ta SET id='.$id); 4 w$ N( f1 Z( O' _* d9 G
- }
; c. I, j4 n0 L# i. U3 [ - # php的文件锁,释放锁 8 B4 \% c2 i; ]% [
- flock($fp, LOCK_UN);
4 P* l5 X' B+ d - fclose($fp);& E5 t4 y# m: E- ?: f1 G
- ' I. I8 {/ p/ i/ D
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
3 t. p/ Q) F' i, O# N7 V8 @ - // while($row = mysqli_fetch_assoc($res))5 y* v8 Y' q" `7 b% E! f" d Y7 B
- // {1 d w7 F, \( C* Q
- // $id = $row['id'];
2 |' I9 {; a2 f - // }, e9 I. E( H5 O% Y: n& V
- // echo $id;
复制代码
" x& N* V& P& b/ C- Y
% P* F* m6 ?9 o2 N! z" k+ C/ A抢券活动实例: - public function envelopeSnatching(){
1 a# S( N g7 G- u - $lingqu = $_POST['type'];
, d/ j7 c& v r - $uid=session('u_id');//用户id: `+ s# {+ p6 u
- if(!$uid){
* C, @" A7 r$ x+ e# h/ @" i - $data['msg']='您没登录,请先登录!';
4 }& D" l* T# C w, K - }else if(date('Y-m-d') != '2017-12-12'){3 j7 s% h! n$ [
- $data['msg']='不在活动时间内!'; j9 A- D! S* C) y2 p: p( P+ A' \5 g4 \; V
- }else{
- ^* k( e6 ?' l: x9 h/ r - $hours=date('H');//当前小时数
, v/ F% r$ C' h& l4 Z - if($hours > '09' || $hours > '17'){
+ |- j3 L: F& @' j2 N7 P& i$ I
, u2 F: X; M5 g8 g0 d9 b: n8 u- V% q- if($lingqu == 1 || $lingqu ==2){//点击10点的0 m- b: B# D' P, ] G
- if($lingqu == 1){. N: Y) G# q' I" J. t& Y; F
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
6 o2 R3 f: m0 ?: X6 q: H& ] - if($hours > '09'){+ U, F% `2 S4 H# t* V9 u1 `
- $num=mt_rand(25,28);//优惠券金额+ ?" Y, l r- w+ ]0 X# T7 B/ }8 w
- $id=1;
) ^7 B3 z% c7 I5 ]1 [" x - }
" J7 j2 x; A! g2 q* e9 m. b2 h - }else if($lingqu == 2){
! J( n/ ]! k* ^5 U0 r) M - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();1 j0 J! k" L' h, a) J
- if($hours > '17'){2 E; C F: K( _0 s/ W+ }
- $num=mt_rand(50,55);//优惠券金额
Q4 _! n+ F7 j l# l" E - $id=2;+ Y' Z. J# T ?. H) }; x! Y
- }
4 ^; P7 z3 `( Q" l8 L& v8 q) G5 a - }4 g2 [; D) q. E [! u5 g; r
- if(!$id){
8 J U+ i. y1 K3 g0 t3 ]$ J - $data['msg']='时间还没到,晚点再来吧。';
. w4 W1 H; s- S2 @ Z - }else{
) {5 L5 e1 P8 C2 r& ] - if($is_lingqu){
4 Q2 w, o( @; c5 U5 Q x - $data['msg']='你已经领取过了,留点给别人吧!';
: L+ I o( V6 Y; l9 b4 z+ j' O5 o - }else{2 N' i. Z+ @, i
- //锁表
- T6 b$ C5 g9 M+ q% Q: R2 |) v - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');8 `# z- K- r7 m% x& ?) u) T' C
- $active=M('active_num')->where(array('id'=>$id))->find();
0 T! \% G4 M' T, | - if($active > 0){
! f" j% I' I5 [ - //开启事务
6 h1 N2 Z& i7 Y9 T4 ]* E- \ - M()->execute('start transaction');6 Y1 E/ e3 ~) Q5 a" E7 z
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
c. D2 W; b) O! L! J2 n - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
, v: Q' C* j1 ] - $members_preferential = M('members_preferential');
: q7 M8 B0 |# I4 x: ~, s - //对应投资金额,3 R' h S/ ]$ j% i
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
; O& N1 h# h( [# ] - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');2 k/ v2 w6 s$ h: U6 Y" I: x/ \" \
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
* `; z3 v5 ?& I/ _' V - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
3 ?. c7 k' V# u3 n - if($save && $add && $add2){
1 E8 }6 A# s' M8 h - //事务提交
6 r6 R9 w% @; R, R$ A: p! o, o8 { - M()->execute('commit'); H0 X6 E* S, J! V! O* x
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
0 {* ^% i, p% u4 K - }else{; ^2 [6 g+ r# S* ^) H* u
- //回滚
9 ~2 o$ G# i1 e, P% @ - M()->execute('rollback');
% p0 W+ o' d6 t! z% i$ y - $data['msg']='未知错误!';) O* ~1 ^9 U) r4 T" f. r4 g3 L
- }8 d( w: ]1 `0 g; ~1 y! j4 ~
- }else{7 o1 [7 ]( V# d3 n! B
- $data['msg']='红包已领完,你来晚了!';
6 B7 ~* W4 A6 Z2 b0 n% b. [" T) {0 ^* K - }% u$ d! P+ I. ]" a4 ^( Q' ^
- M()->execute('UNLOCK TABLES');
3 V. V, q3 ?& c5 M - }
3 \$ k) ?5 j$ t% f; Y - }" F+ Q) u! b1 u
- }else{
+ I& ?; e1 n# P7 C) B+ \7 E$ A. j - $data['msg']='非法操作!';
* X% a1 @$ ^: Z I8 H& V. d! v: _5 v; ` - }0 e2 ?0 h( e( s1 S$ s5 d
- }else{
3 ~( i& Z9 E0 K - $data['msg']='还没有到活动时间,请晚点再来哟!!';; H; S* m2 {2 C& k3 z7 c" |
- }
) R. p) [1 l, G6 ] - }
2 M0 Q' Q+ l& t% h - exit(json_encode($data));
3 B( W9 J& s( |" { - }
复制代码 6 L' [: |5 u/ G. ?! P! x m
* r) a5 {5 v2 w6 x; |, ] |