|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 5 I* x; g6 ]1 Q3 S% C/ L( f' W
Mysql中的锁语法:' V q1 N8 Y( g( F
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】# y) A7 x5 r3 s* P
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
* P! x8 f- f6 T4 X' h1 x8 tWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
" \5 |7 G0 p ^0 {6 m. L注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
' l W% l* x7 e) W4 @; H# [, `文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
) F. b$ O4 E9 i W" U0 u5 |. f$ |测试时,有个文件就行,叫什么名无所谓 总结:- W. C' m5 [ s3 r' @ F- y8 J6 u J
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
. M/ |6 v; w5 Q/ c1. 高并发下单时,减库存量时要加锁3 d3 X9 ~( C. R/ n8 f8 z
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
2 g! Z& q5 I n9 p. [ - 模拟秒杀活动-- 商品100件# w# ^* b7 T/ G; {5 p( d
- CREATE TABLE ta
: ?1 N- X9 J" l1 ~9 Y& F6 U1 D% J# y - (
0 s$ L; K' o6 D: }+ B' O - id int comment '模拟100件活动商品的数量'
( ?& f7 M; j8 b6 [, H' V - );2 o5 C6 r% n s- l. ]0 [$ a. O0 J
- INSERT INTO ta VALUES(100);
6 ^# X, K+ u: R/ ^9 w- [8 ] - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
+ @: ^! q- h3 ?9 F( D5 d+ A - */ 3 M3 R# l2 l* c5 P6 A, s) h! G
-
+ ?4 w6 O( G7 C. {: z2 P - // 关闭错误报告
+ B/ g' a2 G9 I - error_reporting(0);
- N1 M8 _: e7 }$ u8 b - ; j: H7 J- l" o/ ^. w
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
% J8 f1 V7 |0 x* l* H8 } - $dbuser = 'root'; // mysql用户名6 h- H6 ~7 G/ e2 y/ U: J
- $dbpass = 'root'; // mysql用户名密码
% w. D7 K0 {$ C; k3 z$ r& @- m - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);2 W" ?4 @5 e- l A3 E1 ^
- if(! $conn )5 J5 X; n2 c" z9 @0 [# m/ Z
- {9 p K" p$ \; o- @9 C
- die('连接失败: ' . mysqli_error($conn));. }/ k( E! z: u' C, ~! y
- }
2 X. m3 \$ K5 {. o7 ` - // 设置编码,防止中文乱码
: g- M, y% k: E, z/ h - mysqli_query($conn , "set names utf8");. e% \* o7 C% D6 b$ |
- mysqli_select_db( $conn, 'temp' ); + ~* x; z) j( U8 a& |6 W9 @3 v$ ^/ K
" |* n/ T2 z7 }9 W$ |- B: Q5 ]4 r- |- # mysql 锁
! n \! p# o0 G' G' V8 {4 G, X - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 6 ?# R0 D! |8 U8 S. L! d
- $rs = mysqli_query($conn , 'SELECT id FROM a');
7 Q' ?% x% N) k8 @, I6 I7 R - $id = mysqli_result($rs, 0, 0);
6 P+ q6 U/ e" j% w7 q - if($id > 0)
# W6 ^% _" j7 @ - {
" ^. Q" T* o: z3 r$ D - --$id; 6 f9 H+ k4 x- _! T, B! k8 _
- mysqli_query($conn , 'UPDATE a SET id='.$id); % j3 D. n- E& h) K/ u6 q: w- W" l9 v( Q
- }
9 F) }4 t1 M- Q - ! K k' i' t" O/ O ^
- # mysql 解锁 m. d" B. @7 ?: f; R6 U" L1 O
- mysqli_query($conn , 'UNLOCK TABLES');' f4 v3 J; c. R+ w0 H
- //查询解锁后的id值% r$ o+ j- _6 S. p4 y
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
2 t1 [' v+ L+ p( l# Q8 x - // while($row = mysqli_fetch_assoc($res))1 j) N2 h+ g3 B1 ]7 K4 X& C
- // {
1 U; z$ K& _2 y* \ - // $id = $row['id'];
* w/ Q, L a! r+ q - // }
$ J, F0 C8 L; m, e' v - // echo $id;
复制代码
3 b9 q4 \5 A3 t* F
; s# ~( \7 }9 \: ]0 F9 N8 `; q% X5 n" A( j4 P* y
PHP文件锁示例: - /*
2 K( X& a* D c t1 b3 K - 模拟秒杀活动-- 商品100件" F4 C# Q% V, ]. m ]" [
- CREATE TABLE ta ?. L; P& U" Q1 G' l8 u8 s
- (
( W% S; T+ i( u' ` - id int comment '模拟100件活动商品的数量'
: \1 V3 G7 l% G. q7 h& o$ |7 N - );
2 L6 r) g2 W! h% T# T0 N - INSERT INTO ta VALUES(100);2 N: a0 K! c- B* F- E* o% s) Y
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件% j& F2 S$ R. D
- */ 8 ?. P4 E$ _) e9 E! L- T, p
- * w! |- `/ d+ o: R3 k
- // 关闭错误报告
& |( m* ~# Z& [2 D _# K - error_reporting(0);
6 k- P# t+ y6 \" [7 O- l0 t4 y
% [6 B1 Y9 P) E6 h9 I' N- $dbhost = 'localhost:3306'; // mysql服务器主机地址+ Q8 P9 D/ ]( X7 m; U9 ]8 u
- $dbuser = 'root'; // mysql用户名
2 B- o4 ]& z' g( I3 s - $dbpass = 'root'; // mysql用户名密码; S1 j" }! I3 B/ \: J5 v
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);- ^* I7 e# C! w+ U4 n: v
- if(! $conn )/ s4 K: y. v5 N; K3 s& p: H# Z
- { L7 E4 Y# B4 R' q% r$ V
- die('连接失败: ' . mysqli_error($conn));" |4 L4 l# M. r. T" d( T7 ^8 m
- }, K, F- H8 {6 `+ N" O
- // 设置编码,防止中文乱码
4 k0 u' @' ] N T, T' p5 y N - mysqli_query($conn , "set names utf8");0 s' D6 H1 [$ r# A1 I
- mysqli_select_db( $conn, 'temp' );
1 R0 W% D& a/ U- ]" g( ?1 [6 K* Z
* ^, t4 M. d/ `- # php中的文件锁 " Z( _. j& @8 k* B" E6 }
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
* k- y6 S2 N8 R - flock($fp, LOCK_EX);// 排他锁 0 f4 ~6 U% ^! j( o" E
- , d- U+ u( N; R, P
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
; M' @7 o8 z3 i9 `6 g0 @+ s3 w - while($row = mysqli_fetch_assoc($retval))# L* @: k+ c* Q* s! @2 W
- {
; w3 P' V1 g$ u7 t% Q - $id = $row['id'];
# e1 A- y% X9 k: c - }
# r1 J8 _2 m0 e) u: P. @) Q -
( s, L# O) @0 t - if($id > 0)
0 o$ Z# r6 O9 W7 O - {
( k- j5 Q# u @/ @ - --$id; ( W6 s- V" x( y8 M2 `: H
- mysqli_query($conn ,'UPDATE ta SET id='.$id); 1 Y% U0 i& a- y2 s7 L
- }
3 c$ _+ I* j6 `- \ z2 | - # php的文件锁,释放锁
( c; b4 Q" K5 \ - flock($fp, LOCK_UN);
( ~7 }* `& G) U3 M6 d5 [ - fclose($fp);
1 G0 r/ s; p+ @; j+ v5 S - 8 S8 ^/ e5 ?7 H8 f% ?
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
/ i f: z8 C* _ u' u0 v - // while($row = mysqli_fetch_assoc($res))8 `$ A) c5 A! e% M
- // {
7 ^, u. ]) F' g) ]: u, D! q - // $id = $row['id'];
9 g6 Q8 G q9 j+ Y - // }
9 J% m3 V: _1 W8 G: n5 m - // echo $id;
复制代码
) z6 g7 w! i( I* z2 n) J
- ~6 w1 p0 o: [0 K6 ]$ d抢券活动实例: - public function envelopeSnatching(){7 `% Q( X8 i0 ?) [- @
- $lingqu = $_POST['type'];
$ Y! W8 }9 t" f, @5 c - $uid=session('u_id');//用户id
' w0 w Y6 W6 c, B - if(!$uid){
0 ?- X9 `$ D5 g1 I# C( U7 m - $data['msg']='您没登录,请先登录!';
! m3 ^: k( ]) ^- O! E3 } - }else if(date('Y-m-d') != '2017-12-12'){/ Q" I T. R0 _2 Y9 W
- $data['msg']='不在活动时间内!';# o' p0 b2 b$ s& ~
- }else{
2 q' d) c+ p; A0 [ m4 q. `& o' C - $hours=date('H');//当前小时数
: C' p& w1 W: G& D4 j! Y2 k. | S/ q - if($hours > '09' || $hours > '17'){
4 \) Q4 U6 D, R3 E9 B. w
o- E) O, V2 M" s& X7 d2 S- if($lingqu == 1 || $lingqu ==2){//点击10点的
7 J1 @* j& x! ~ - if($lingqu == 1){
4 r. {# r0 ^: }3 t8 ` - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过! d6 U7 d; N2 V$ u7 l! d
- if($hours > '09'){, L9 X! K' ^4 d& H& a" g6 ?, g
- $num=mt_rand(25,28);//优惠券金额$ o* c! Z/ G% \* L/ h" q
- $id=1;; O4 m# ]' ~1 o
- }
- @: S6 ^) w4 ?1 f+ L - }else if($lingqu == 2){0 t& U& S8 q0 o7 C* I+ S* {
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();8 N0 ?( i3 r# _
- if($hours > '17'){
/ O8 t* i; T2 ~" |5 F. i - $num=mt_rand(50,55);//优惠券金额! P& N( A' t1 v) M
- $id=2;
/ ]; j; Q, k* m+ q2 d- S - }
, `# V1 R9 d2 i$ C8 z1 R5 W - }4 c/ k$ ?8 m* j# a( j* i" V0 i8 g
- if(!$id){/ r4 i: a, e/ ^) c7 i8 z+ g0 Y
- $data['msg']='时间还没到,晚点再来吧。';3 B. {% `4 j( |( `
- }else{ S7 X: O4 M1 Q
- if($is_lingqu){
& Z& u" H5 T/ z. A# y3 @3 B M( J8 g - $data['msg']='你已经领取过了,留点给别人吧!';, v4 j% ^. `* _6 L1 I" d# [
- }else{
# m* M- V# r( k* ^ - //锁表! D! n b: ]# w4 s
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');- t& N" @- f" D5 E# w1 X* W
- $active=M('active_num')->where(array('id'=>$id))->find();
' X/ s9 A- e; b3 Y; l( j - if($active > 0){
4 X& N4 T, e( T: u: h; _ - //开启事务
1 ~$ u# N9 g, N" s1 m - M()->execute('start transaction');. v J8 W& a+ C* g$ _% N
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));4 V1 V( B8 P7 J3 N; h
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
6 u+ M1 u, k3 ^ - $members_preferential = M('members_preferential');0 `0 ]5 u/ t' N: n4 K2 @" j7 b& K
- //对应投资金额, `6 |4 H/ j g% X" ]1 o2 [
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
7 H, ]% i& C, t/ r ` - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');$ H/ k L6 a; s c: {3 P N: s
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
6 `4 z* D2 Q" G3 K - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
! h* q6 l, `( V1 g - if($save && $add && $add2){" ~0 I" O$ C" F+ ]1 L& C% M
- //事务提交
; Z- R) m! j& z. T# ?! P - M()->execute('commit');
7 g# l2 c" a9 w - $data['msg']='恭喜你领取了'.$num.'元优惠券';
9 d/ {: T2 R& y* i) A U( f( f$ Y - }else{
# ~( Y/ H- |, R - //回滚9 d4 y7 E+ Q0 R1 r: X' f$ g
- M()->execute('rollback');( l& O' L0 F7 x+ B
- $data['msg']='未知错误!';! ~. P& j( N3 S4 U
- }
/ s! R* T, n; P - }else{
2 o( J \' D/ c, a( _4 X - $data['msg']='红包已领完,你来晚了!';
6 [8 _4 j& x! k$ j - }
% y4 K+ `* _1 p, i' n - M()->execute('UNLOCK TABLES');
6 h. b0 B. a1 S3 ?$ y$ v4 M - }
+ q7 s$ h! S0 i3 S, |5 J! l- [. Y - }
' E. T8 T- l8 V! l( o/ g - }else{5 W7 z* j) D7 @" {7 `% ]
- $data['msg']='非法操作!';
$ C$ ?8 m# F6 T" i - }
: W8 M0 M& x/ e2 g* D: Q# ~" Z - }else{
& ?& m$ z# X( L: w - $data['msg']='还没有到活动时间,请晚点再来哟!!';
( n3 G+ m: {+ v7 I$ p3 S. O) a" B - }
3 V3 z# t- t7 j7 j' H" p% Q0 | - }
; `8 ~7 W/ ~' t3 Y8 d) g - exit(json_encode($data));
( Q. w: Y" c' t2 Z8 S" Q - }
复制代码
* k3 G+ H; p0 }
, z3 [* q- t/ `2 t/ j |