模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
8 Q8 O$ ^5 H: s+ }8 u- M Q: e; lMysql中的锁语法:4 h1 C' b8 o% c1 N, m0 ^6 i
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
0 B9 U: g! C& B7 J% z) [) CUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表5 \ t; X! E ~0 q
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞) C% }+ L3 j3 _: k3 M$ G5 i
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
) P G$ h4 d: v. D. S文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。% v$ I1 {# @8 t/ d
测试时,有个文件就行,叫什么名无所谓 总结:6 v* N4 r5 j1 r
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:' u0 [! i1 G* o( \+ l
1. 高并发下单时,减库存量时要加锁! Q* v! t' P: R2 V
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
/ r# P0 j% L0 r6 W$ x - 模拟秒杀活动-- 商品100件
# M7 A! n/ C% S4 R. }5 |8 x4 P - CREATE TABLE ta& V$ G7 O0 i7 y% {0 j: z" k
- (
1 m: E( ~' |# V; Z6 e+ I - id int comment '模拟100件活动商品的数量'% b5 i* R2 W# N0 _% Q3 \1 m/ O9 |
- );# }3 K4 Q! h, M' K0 h6 {
- INSERT INTO ta VALUES(100);# b7 r0 L7 j0 z7 Z5 Q5 D
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件( q/ l; M, s x7 ]
- */ ; q6 ]' r! q; s$ J) `! U# E- x
- / K( B" c$ O' ?" Z( R; P5 c
- // 关闭错误报告5 o& _8 v8 q; i- s) o
- error_reporting(0);
; ?+ l& e" x8 m% p; D
* g; z2 A) v O7 b- $dbhost = 'localhost:3306'; // mysql服务器主机地址& x7 }; f: g" X# {
- $dbuser = 'root'; // mysql用户名( }4 Y0 r/ [' R5 \9 i
- $dbpass = 'root'; // mysql用户名密码8 r! O$ e' n8 ~4 s: Q' ?8 a
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);3 _5 L) j1 X8 u" o' K& F
- if(! $conn )
) i8 v$ m: ?$ L$ J1 `5 D - {
6 i3 f" e! w! ^! l - die('连接失败: ' . mysqli_error($conn));* M9 I3 c7 a: m. v# t
- }
+ s9 b4 ]( Y5 `" J: }8 o. j% W - // 设置编码,防止中文乱码
" Q5 ?( U/ b% T5 G0 T; { - mysqli_query($conn , "set names utf8");
7 `# ?1 ^7 o+ L6 v7 Q9 V- P. e - mysqli_select_db( $conn, 'temp' );
) A+ r1 l/ a4 y2 S, b# a8 ]* Y; M; Q
" w# _ ]6 B- e& ]' a- # mysql 锁
$ `; v) i- w) ~3 C - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 & Y, R3 ^0 t% k5 m
- $rs = mysqli_query($conn , 'SELECT id FROM a'); ( u+ Z' G- K7 r5 T
- $id = mysqli_result($rs, 0, 0);
0 T6 S2 U2 V2 _% o. ~+ l4 j - if($id > 0)
' v0 t, z; r# ^. R9 R3 s - {
: I4 ]! H/ j2 L - --$id;
: c1 p+ X( W) x& H( f/ r S3 F) m - mysqli_query($conn , 'UPDATE a SET id='.$id); 0 K9 k) E6 \( @5 m: b9 E/ c# a
- }
. U N: G+ e8 w; T# K
) l0 X" d4 ]6 W6 V- e! b- # mysql 解锁
# c, A# d- b) C8 Y4 |3 ~0 k - mysqli_query($conn , 'UNLOCK TABLES');
# R+ Z/ x0 U+ I+ {" z: P - //查询解锁后的id值' ]" x) @6 J/ D" w$ `' c7 P P8 I
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
3 ]' x' L3 F3 W4 k4 E9 w" Y+ e; d - // while($row = mysqli_fetch_assoc($res))
" {. F$ p5 t& J8 O/ Z - // {/ [7 x% w& K4 U
- // $id = $row['id'];
) P8 c; h2 t- A7 F - // }
" p( o& Z+ q. ]2 S) [# J - // echo $id;
复制代码 ( m% T w+ D2 n! Y- B% n8 u' _3 ?2 U
0 n- o$ x$ d) {' E& t! v
3 c1 x$ @. F" O h8 _# m
PHP文件锁示例: - /*
" F8 X' k! s1 y+ g - 模拟秒杀活动-- 商品100件1 ]. h2 n$ q6 n, |: j/ O
- CREATE TABLE ta
% b' z7 s. L3 I! B, M. N - (
$ q% V$ h6 i9 n9 S" u - id int comment '模拟100件活动商品的数量'. \9 F; P- D+ d
- );
: Q. v g$ Y; P( g! I/ j. Y - INSERT INTO ta VALUES(100);
" H- I1 {; R$ O# P8 \9 R - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
7 W1 F0 c" V! j) c! b& K - */ \ L4 D+ V2 H7 d8 {
- " D5 S! O# C% {: K$ ?; u9 M: i3 R% S
- // 关闭错误报告
' ^+ `9 S7 |, `; N% A; ^2 B2 L0 e - error_reporting(0); $ T/ p; p( v+ p" p0 _
0 W3 M, b4 r/ g: v0 f, u- $dbhost = 'localhost:3306'; // mysql服务器主机地址( M. t, L( C2 M. O# L5 t
- $dbuser = 'root'; // mysql用户名" B/ B/ q- j" Y$ `
- $dbpass = 'root'; // mysql用户名密码
, m5 v, C" c8 @" |. N - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);2 s% N: N% G1 v" O
- if(! $conn )
0 c" E/ S" `' X1 J: q4 D9 O" e, V - {
$ C8 N h( v: r4 S1 i* E - die('连接失败: ' . mysqli_error($conn));3 V+ k/ E( Y/ k+ r& y" V
- }
$ Y- v6 c, ?, x( n6 B+ P - // 设置编码,防止中文乱码
! o3 u8 o7 t) S' p. Y) d) g7 h" ? - mysqli_query($conn , "set names utf8");3 p, R, F$ v3 |3 ]/ |) b- t4 Q9 z
- mysqli_select_db( $conn, 'temp' ); - @- y( `+ d+ S. d" g
* u2 p, k" a; q0 a0 ^' h, T" R9 O- # php中的文件锁
3 y+ H' a- C/ I7 P - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 9 e5 x4 F' F3 U$ w2 \
- flock($fp, LOCK_EX);// 排他锁 ! |! A: K- S4 X8 n* V. e }$ [1 _' R
. v+ M Y. q; u- $retval = mysqli_query($conn ,'SELECT id FROM ta');
- m* s7 m$ D# T. D - while($row = mysqli_fetch_assoc($retval))
0 a& ?4 D* a' Y9 J3 T+ r - {; ?8 E J9 D0 a/ \( Y$ m
- $id = $row['id'];
/ V$ B5 _+ l# q9 x9 {, B+ P - }
% H/ s6 C+ B) z" V2 m -
c& f$ k0 s& q8 ? - if($id > 0)
1 r+ s0 ]8 A) ]$ Z3 m6 F9 j: | - {
* j7 F, d/ b$ ~ i. X/ ?* A - --$id; 0 j$ x& t$ m. ~7 m3 [" k1 q
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
+ k) [6 }% N; U - }
% u& f) J7 [. g! I- b# r - # php的文件锁,释放锁
# S; V9 c( N' Z. [, e* N' _ - flock($fp, LOCK_UN);
5 D: H: o/ U8 z! P3 v: p g9 p - fclose($fp);
8 I9 v$ q. a- \
k% L8 C6 I* N- // $res = mysqli_query($conn , 'SELECT id FROM ta');
5 ~, V1 `& r" G9 S3 b' q - // while($row = mysqli_fetch_assoc($res))* E" @8 G2 M" H* l
- // {, \* w1 J0 o: d, c( f
- // $id = $row['id'];, C% A3 ? g( E2 d1 N
- // }: ^% Z+ H6 W! R5 g7 |
- // echo $id;
复制代码 G8 E; _' Z( r+ k/ z, F
( w% [; N$ [/ Q- R抢券活动实例: - public function envelopeSnatching(){
3 P; I1 f" e0 O u, w - $lingqu = $_POST['type'];
! H g8 R( D( u6 T& Z - $uid=session('u_id');//用户id
; U& N6 a; ?6 v/ K' Z9 L( Q - if(!$uid){
3 |6 ?# \ L* Y9 a* j- d2 J - $data['msg']='您没登录,请先登录!';& H% X& U; `( b
- }else if(date('Y-m-d') != '2017-12-12'){6 s6 f8 ^% { M4 U b# R
- $data['msg']='不在活动时间内!';
8 {1 f& P. X/ t4 Z6 p0 i - }else{' s! l9 O' d4 [4 r
- $hours=date('H');//当前小时数
5 X+ w2 }8 l# r+ S ~9 G8 b - if($hours > '09' || $hours > '17'){9 d; ]7 U3 G" ?2 C( |) m6 l
0 P0 T" ]- d$ [/ L- if($lingqu == 1 || $lingqu ==2){//点击10点的
, O: I" a; w3 I6 w" _ - if($lingqu == 1){3 @/ r% Q3 ?8 M/ }
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过/ T$ a% r1 C, d8 x5 Y
- if($hours > '09'){
& |6 u- I, _$ K1 r& B: w - $num=mt_rand(25,28);//优惠券金额
& I3 [" H* C2 h( | - $id=1;
Y$ L4 {3 o+ _/ c8 ^7 ? - }
5 ?4 K# s: F6 y - }else if($lingqu == 2){# I" t' L! U6 \/ q4 z8 V8 p
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
6 W% ], \+ i& t3 x, P. A9 _/ k - if($hours > '17'){5 A' U: J4 D0 S( Q: h3 [ J: e
- $num=mt_rand(50,55);//优惠券金额
: t" T5 }( r7 D$ ~ Y - $id=2;
- [# q1 @, D' j - }
; c8 ?5 s1 r: R4 g! w - }
/ q$ j* ]. L! I - if(!$id){3 `1 ^ w- ^2 y( r% q" U; F" Z- \
- $data['msg']='时间还没到,晚点再来吧。';1 | i. L) [7 r6 G
- }else{& R& m7 G. k& u6 F1 C% S2 x
- if($is_lingqu){% |6 g6 h# g# c( V' k
- $data['msg']='你已经领取过了,留点给别人吧!';
" }( ~# Q, f' p9 y) U1 R - }else{$ \; x3 E# ^4 o
- //锁表' ?9 D- K" i5 r, H, B# L& l
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');. X2 e- y, j& S! h- k
- $active=M('active_num')->where(array('id'=>$id))->find();/ r6 a1 o! R; j" x( p7 I
- if($active > 0){
" ~# e2 q" p% k# M: `* G* l - //开启事务8 K# O1 G+ R9 t) k
- M()->execute('start transaction');
$ L1 u: I6 ]" a. ] - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
- ?; Z5 H# i0 j - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
O& l' A5 P5 [! z7 K. I - $members_preferential = M('members_preferential');
# I# i7 Q, S* E o8 l" S& K0 Z - //对应投资金额,
! t% @/ E) {4 |0 F& C% e' e - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));* ]% c C4 d2 ~: m, I; Y v
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
8 ?- B7 \- Y( K P% | - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间! h% ]; c' R: W% y
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
O+ r& o1 T+ z: @. W. b - if($save && $add && $add2){
! V% |$ ?0 L- g) i( c$ J9 I - //事务提交 y5 O* G$ Z- }6 B5 Y. |: V
- M()->execute('commit');# u3 G; ~0 `# S0 p
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
; E& g3 e& \! A' y t; h$ c - }else{
; D9 V( g" K. T# s/ e - //回滚
, T; p/ D) d- w* P `1 q - M()->execute('rollback');
* W4 J$ R' t a& q - $data['msg']='未知错误!';
" s* c6 T$ [5 B: ~3 _8 k - }6 _3 D3 `, F8 a7 e( ]# w
- }else{- Q' C) ` O6 U5 Y5 W. x1 g
- $data['msg']='红包已领完,你来晚了!';& v% Z5 n( e& z# j0 t2 l
- }
4 D/ W* s4 t7 R8 b - M()->execute('UNLOCK TABLES');* o& ~- ]; d" \ p, k4 r
- }
% s2 P: v8 m9 B9 i - }
1 b3 Z- ^- n% x& _" A/ ] - }else{/ j: Z- @6 g- W: F
- $data['msg']='非法操作!';
m# `9 |4 i. ]- b; j' o7 k - }; j3 G% q& \2 Z0 ~
- }else{# ^, ?* e, b& `% d3 q% k
- $data['msg']='还没有到活动时间,请晚点再来哟!!';* n: j, v8 z! V r. d
- }1 E* j) ~3 n) s0 w5 c; A" n4 m0 |% p
- }
0 l$ M) M+ V! g g9 u8 x0 p - exit(json_encode($data));
5 A% c) e0 n$ u" W2 _& _ - }
复制代码
K* w$ D/ P- O9 m1 w
; G& `: h7 B# c |