|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 ) e' p( Y ^3 t& O8 m
Mysql中的锁语法:& r; I- a& V$ s! y) Z# v0 y
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
2 [6 ?2 p" R% a5 A, O1 \9 o; BUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表" o: i0 @1 v1 e$ t3 I
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
- ?9 a& E& B. V$ Z. d) T& s注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :% i. c" G/ W* Y) J
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
- S$ s/ A% k4 S+ U8 z" `7 Q测试时,有个文件就行,叫什么名无所谓 总结: }* n: C- p# t) B, p6 }8 P" Z& U
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:. A7 g- o2 {1 v2 F% T' B: @
1. 高并发下单时,减库存量时要加锁' E) U0 e4 Y% I* d* v
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
2 ~& h8 p1 h" h! g( u+ L* q8 b0 w - 模拟秒杀活动-- 商品100件
6 ]; I0 D3 H4 P. | - CREATE TABLE ta
3 Y& D3 t8 N/ t - (
; |# J" u# Y0 p% ~, v, e5 x9 v - id int comment '模拟100件活动商品的数量'% @' n# K: q/ d% i
- );" Q% z+ d* c ~5 @
- INSERT INTO ta VALUES(100);
( i- H, q# K9 r0 I% q. l7 N* { - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
F: n2 o4 X% h( t - */ - e' L: W- h# ?+ M! S0 g
- 8 x( w! C' h4 D9 b& @
- // 关闭错误报告, s% g7 G# D) Y7 K. I
- error_reporting(0);
" [7 }% B* S& Z
* o+ n. Z6 l) S3 L- $dbhost = 'localhost:3306'; // mysql服务器主机地址
& |9 N. X' W8 L( [1 f( F; F8 z - $dbuser = 'root'; // mysql用户名
( k; l/ e1 C$ n2 y0 q - $dbpass = 'root'; // mysql用户名密码
6 k8 v& a* D1 Z; d - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);9 k4 I; X' k/ Q, @% b
- if(! $conn )
& V3 `9 Z# @; B D - {
# Y, {2 R* b; M# _ - die('连接失败: ' . mysqli_error($conn));
. G8 G; { ]! \4 }' p - }, Z; A) a* k1 A1 f+ `4 p+ N
- // 设置编码,防止中文乱码
% E3 u4 s2 c7 L( X - mysqli_query($conn , "set names utf8");' ?% q: h& H0 ?! C2 k
- mysqli_select_db( $conn, 'temp' );
2 e+ H$ n, E, `
3 p/ o6 a' n, {' ~' ]+ U) o( t- # mysql 锁 ( s6 t$ W# }: I0 t0 u" x/ j
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
, R- [ O( M$ G, C2 n3 V) I - $rs = mysqli_query($conn , 'SELECT id FROM a');
4 r! u' p- N2 a; T" k e7 T - $id = mysqli_result($rs, 0, 0);
4 n) `5 n. W) Y' |* H& K# A7 g - if($id > 0) l: h$ {# j8 Q2 V/ N
- { * J6 U. e# ^6 A: J/ {
- --$id; 3 r, k8 q3 M0 i; E
- mysqli_query($conn , 'UPDATE a SET id='.$id); ; Y0 w. q9 V3 `9 `# r3 A
- } & z9 Z6 \) ]% N* v% d! q w! \
: v1 p7 ?5 J) L, M) h8 L- # mysql 解锁 4 r; c: B3 A8 l" p% t1 y R' y
- mysqli_query($conn , 'UNLOCK TABLES');
( V; y1 G. A' A1 F/ _6 E3 Q - //查询解锁后的id值
% e; S3 J- Y! c& j$ B - // $res = mysqli_query($conn , 'SELECT id FROM ta');
1 R3 q( ^5 d6 Z* y - // while($row = mysqli_fetch_assoc($res))
( |* b3 N" t! O. _2 O3 _" T - // {/ x# G4 y' X4 I& W, I
- // $id = $row['id'];! q8 C, U8 K6 C7 c% n
- // }
0 }: H1 I2 h$ a - // echo $id;
复制代码
- e, W! K2 `' Z' _" R
1 z. r+ S" W7 z7 k, f1 t7 }6 b3 G0 o3 G
PHP文件锁示例: - /*- G* X5 V+ d- C, `6 k- x1 t5 C! f
- 模拟秒杀活动-- 商品100件
5 S0 Z9 N7 `: g- C4 h - CREATE TABLE ta
$ R5 @! h2 T$ o8 K, p - (
' O1 ]1 K6 g# r) L6 e6 u; U0 v, L - id int comment '模拟100件活动商品的数量') j y$ _. c/ O/ M
- );
. R( b# K6 k9 f, b0 h, I) D) I - INSERT INTO ta VALUES(100);
) U4 k- V4 d @: H8 S - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件3 r. |8 k& M, O7 f( \
- */
* X( C! U6 b. f4 n1 x& Q - O+ C3 o# ^; B! {$ v/ ]1 R
- // 关闭错误报告
) |4 m2 k4 ], v) W. k - error_reporting(0);
( g' [+ \. _' q. Z; f: F* ] - ! U7 r6 m P1 d
- $dbhost = 'localhost:3306'; // mysql服务器主机地址" _6 K9 i9 ^5 j9 v: ]
- $dbuser = 'root'; // mysql用户名( {4 A; A" U# i' }
- $dbpass = 'root'; // mysql用户名密码+ P1 l& r. d& O/ w9 s1 U9 }, c
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
" A# s9 A4 O& i" s: U9 Y - if(! $conn ): N: f! B) S6 N) U
- {
2 J: ?( J: x: ]* s l3 D# h1 g - die('连接失败: ' . mysqli_error($conn));
, S- s0 a5 D& K4 g J- a( r8 z4 ? - }6 Z: @7 |$ Z. ^% @
- // 设置编码,防止中文乱码
" L3 O( @4 f# p1 j0 W7 Z - mysqli_query($conn , "set names utf8");1 D0 E0 F2 R# u" m# X
- mysqli_select_db( $conn, 'temp' ); 1 K: y: a7 _4 T% Y$ n- i
. C: [, Q9 ]! i+ w; {/ R- # php中的文件锁
0 {" z( U, O/ w) A5 t - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
2 o% o( T& V1 M/ A4 d& m - flock($fp, LOCK_EX);// 排他锁 ( ~+ J1 E9 d4 B! T1 Q1 S
& t$ f2 P4 J* z9 F- $retval = mysqli_query($conn ,'SELECT id FROM ta'); . q0 Y( U' n9 ]4 b# x
- while($row = mysqli_fetch_assoc($retval))$ N5 K. ?4 ^: m9 r2 i- W: z# i0 S( z
- {
5 m* Y; }6 O: A3 \6 V2 Z - $id = $row['id'];4 _0 y% n6 a+ V3 u/ \
- }
8 Z. K. u: V1 v7 N- \5 z2 q; C" i -
# d& i/ H- N& q1 M6 O$ J+ o6 @ - if($id > 0) 7 M( I9 J0 e. @5 n" Y& [
- {
* R/ `2 c k+ |: Q k' F; m- q - --$id;
- a3 ^. b- R! M - mysqli_query($conn ,'UPDATE ta SET id='.$id); # ?- z% e7 L& v# {' c
- } 8 ?, D8 t0 N5 Q! Y/ U8 a j1 l
- # php的文件锁,释放锁
; m; V1 j: L+ V8 x3 T - flock($fp, LOCK_UN);
, o& p0 V, ], b0 z+ O6 Y/ L - fclose($fp);5 W7 }* q" D+ A3 m; Q
; z9 N- Q6 i$ z" G- // $res = mysqli_query($conn , 'SELECT id FROM ta');4 G; X+ j7 ?: I! j4 b
- // while($row = mysqli_fetch_assoc($res)): g5 A" s) m/ Y! y
- // {
; ~5 J& L7 V5 V% j) `5 N" ` - // $id = $row['id'];
5 b& N6 R' h# D4 A- b - // }
4 C6 \' `7 q& s: S4 b - // echo $id;
复制代码 9 ]: }/ K) N& i3 p+ X
$ b& f2 t% ?+ g! X; O抢券活动实例: - public function envelopeSnatching(){" p; ~; h" p# A( K' p! _
- $lingqu = $_POST['type'];% @) S: k& N$ \3 C& V. l8 [ }) l
- $uid=session('u_id');//用户id: Q; P8 M8 b& a' i7 |4 }
- if(!$uid){1 w% i* l" G/ |5 g
- $data['msg']='您没登录,请先登录!';
0 }0 I/ a+ ^# V7 W - }else if(date('Y-m-d') != '2017-12-12'){
5 w& h. g: Z* ]# n - $data['msg']='不在活动时间内!';
- ], Z( T; Y% @( z8 ` - }else{4 k+ A8 N6 ~7 j
- $hours=date('H');//当前小时数: |/ Q9 }) u4 _! ~3 x( `, H8 P9 A
- if($hours > '09' || $hours > '17'){
6 `& \1 g7 D, p7 C9 W) f - % R: W* D% O2 x" h1 `
- if($lingqu == 1 || $lingqu ==2){//点击10点的% b8 K+ q2 |! t+ g5 k
- if($lingqu == 1){
* |. r, Y. ^. t# h - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过5 a7 N: A* D$ _- G/ f
- if($hours > '09'){! ]/ H: B* C9 Y- h8 [
- $num=mt_rand(25,28);//优惠券金额
3 g& c5 a' J5 w* R - $id=1;: t5 ~7 n' ~! ?
- }
2 a2 ~# ^, j1 T9 m6 L/ j - }else if($lingqu == 2){1 a; _2 c4 [5 e# M4 P' }% R
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
; a3 m) `. O8 ?7 ?: f; L7 k7 A' ~- ] - if($hours > '17'){
. U1 o4 x0 v" |5 V - $num=mt_rand(50,55);//优惠券金额 V" E6 P/ K- D7 z5 `% z$ e) u
- $id=2;
9 E: M$ P& v0 f1 w) v% `# A - }4 t( A( h- I' \& }. K, o7 L
- }
5 b: w' w2 c4 H. W; T# x - if(!$id){
' {. H. J) L) E1 H( _- K' ] - $data['msg']='时间还没到,晚点再来吧。';$ n7 k# x" m3 i0 s
- }else{
0 v9 X* G' N2 f0 e/ v& S3 H - if($is_lingqu){" v0 f* e5 T [
- $data['msg']='你已经领取过了,留点给别人吧!';
) s+ x8 U0 r3 \3 C, }. [+ r& C4 M - }else{
& |- ^( _5 L% w3 o4 @) m8 U1 p' u - //锁表
* Y) S: o- K' w+ C8 [2 n; @) b - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
! C/ r6 Q! l9 R- B - $active=M('active_num')->where(array('id'=>$id))->find();
8 r! Y+ y$ o9 C" ]4 M8 R E, d' \ - if($active > 0){" D& q$ n3 m4 I, q9 n; p+ o
- //开启事务
" ~2 ^* L! u Y! d3 m" | - M()->execute('start transaction');6 \6 c B$ c' o6 Z! k; `
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
" M2 f" }/ A- W1 l! J - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
. f# w" k, K; R3 [# b* L - $members_preferential = M('members_preferential');
' Z( t. U8 p) B5 A5 L - //对应投资金额,
# s, D- l; g% n9 g1 z; G0 ?8 j# \" n - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));% ?0 @2 |' s6 M% q# W* b7 P
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');* {1 ]; w( e b' y% `
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
/ Z8 V# R, Q7 ~* L/ J8 ] - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
2 g. ~3 j$ t. ^( T# Y$ L/ S - if($save && $add && $add2){
% Z" Y9 I) ?5 p/ O - //事务提交
( c4 A6 _5 y$ a) P& D8 D4 o3 ~) v- s - M()->execute('commit');
- R- z0 F. ]1 o+ g( e# z - $data['msg']='恭喜你领取了'.$num.'元优惠券';
8 Q O' _ N: h, i* w0 j - }else{
& u& B' i* x2 ^: \ v4 m - //回滚5 v8 H# ~% |) m, t
- M()->execute('rollback');
2 B9 T, D. S8 B8 j% M - $data['msg']='未知错误!';3 R6 c' c( F# p, f+ t% s
- }: H% z0 k. L& I$ X+ [4 V
- }else{
( X$ W+ L7 O, G4 G - $data['msg']='红包已领完,你来晚了!';' ?( ^) P+ G- U! Q1 \
- }, h+ b2 D- J R1 i6 q% P: Q7 R1 R1 N
- M()->execute('UNLOCK TABLES');
+ f) e; g$ o0 J, C5 _ - }
& {' o& \$ W6 S A. [1 U+ v1 d. X - }
4 w; _! @" d) W, ]! ^$ ]8 y - }else{. C/ k7 V" c8 \1 w" u9 o
- $data['msg']='非法操作!';
# v/ C/ z: a8 I( q4 a - }6 ]; z6 k9 P0 E& g I: R1 M
- }else{ `8 Y* e: O# s
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
0 }% Y* o) v5 C3 R! S - }
3 z$ c0 u& G) M* N4 ?7 N5 L - }
, X, Z& v7 C( n( O - exit(json_encode($data));
! a/ V- X8 T$ q& o+ {! A - }
复制代码 ' k8 B1 z1 V. R
) b. M6 u$ G( j: Z" Y& f" f2 v% m |