模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
; z: {3 G8 f* _/ F. b; F( a) k' dMysql中的锁语法:2 h; m$ Y" U: C1 p B1 N) K
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
% v3 \. ^( A0 m8 }5 b) tUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
5 y+ Z3 b% h; Z# J2 n1 SWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
; O. ]/ p! {5 o- T8 z$ H. a9 C1 S2 R注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
) _% m8 j1 H; J) F. g- U7 H* _文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
% e# O! G7 o% Q, a( n测试时,有个文件就行,叫什么名无所谓 总结:; P+ ~1 y; F& x) i) p6 O. ^
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:5 y+ ~* ~$ `& }2 i- T1 }4 J
1. 高并发下单时,减库存量时要加锁
! B S4 w% r. X. j/ k; U2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
. r$ J+ x( [+ |- c3 J - 模拟秒杀活动-- 商品100件
0 p; l' H: S6 A5 a7 s - CREATE TABLE ta
& k* n7 L* o: A - (
' ~7 K+ g* C4 E4 Z - id int comment '模拟100件活动商品的数量'* [4 K( `& v7 m. t7 K9 q5 C, Y% p6 k N
- );
- x( V- t: O. ^) `. D. y - INSERT INTO ta VALUES(100);; w# N8 t' R% C
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
7 k) G3 i+ s2 `. v' m4 N. g+ h1 y - */
4 ~2 ]7 T8 I4 H$ |! h. w/ d -
; k4 ?( j& ^+ P. J' T- o m - // 关闭错误报告# i7 l! [9 L2 t9 M' t2 V( I
- error_reporting(0); q9 a0 V, M% w: c3 \$ v
- ' D1 g5 T& }! \' k
- $dbhost = 'localhost:3306'; // mysql服务器主机地址* l; }1 n. Z! |7 q
- $dbuser = 'root'; // mysql用户名" ~( c0 u/ h ?. t! l5 `7 e8 ^
- $dbpass = 'root'; // mysql用户名密码3 p$ e. F( }! [$ b' {
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
+ D, Y, {( _8 D5 f" i - if(! $conn )9 A2 @4 S7 S$ j8 |8 n# L" d
- {
* d) C9 o, s0 h" n* H - die('连接失败: ' . mysqli_error($conn));% o a2 Y/ m/ H. s
- }
3 H- z/ P% `- @+ W% o2 {! e - // 设置编码,防止中文乱码6 q" r8 o! ~ g- b
- mysqli_query($conn , "set names utf8"); }1 L7 h% V7 g! H) t7 o
- mysqli_select_db( $conn, 'temp' );
: }3 C/ t: s q9 S6 G8 `
6 ]6 X+ L- w& R' |; G( b+ ^6 X- # mysql 锁 ( i2 p0 E# y( j4 Y( R5 p7 n' j
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 # A' K0 D1 _+ P8 [
- $rs = mysqli_query($conn , 'SELECT id FROM a'); ! v- s. o6 \( k3 C3 q7 [
- $id = mysqli_result($rs, 0, 0); 5 U- [3 T! `- u% [3 b! Y
- if($id > 0)
- z8 q4 t7 y0 l: i- l - { " w. B* y5 b \# j! c
- --$id; ( C! Z: ]; A/ _* w( ^. S! X
- mysqli_query($conn , 'UPDATE a SET id='.$id);
5 `6 d1 u% h% C" K/ Z - }
4 r5 I' u2 n0 Y0 z3 g( q9 ^. t - 8 f% J! I9 T, X( t# `* q
- # mysql 解锁
4 g2 u0 s7 I1 ]7 n0 X - mysqli_query($conn , 'UNLOCK TABLES');2 R1 T7 ~) _" ]7 I Y3 G1 A0 K
- //查询解锁后的id值3 z6 ]0 B' Y- u7 E* r4 |8 a
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
7 y- u9 Q/ G1 \$ [ - // while($row = mysqli_fetch_assoc($res))$ u b: p0 e9 v# w' F7 I5 \
- // {+ y0 O3 G1 n$ M R H
- // $id = $row['id'];
2 j; V% T8 ?( |5 {. j" R/ ^+ m& R B1 t - // }
7 q. a( |0 I0 O Q; g - // echo $id;
复制代码 2 Q+ T m* V- y( Y: d
; }5 }3 y3 @' }$ G1 g7 ?3 h% r1 t( Y2 B
PHP文件锁示例: - /*
% i) B1 q$ g P& G( Y6 e - 模拟秒杀活动-- 商品100件
, B3 I, j* Q1 O& E3 u - CREATE TABLE ta
! ~: `- y' s: i - (
! I+ y- M0 \/ X - id int comment '模拟100件活动商品的数量'
6 m# S2 a$ w) H2 \! p; c - );
5 ^* g( L( Y$ ]% T4 E. Y - INSERT INTO ta VALUES(100);8 Q! E- I6 g3 m0 y6 c4 x
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
L2 Q7 K G( _1 ? - */
9 _3 d8 h- \, M* u5 c -
5 P' p/ v( R# | - // 关闭错误报告 p. s' U3 v8 t3 l3 N. q
- error_reporting(0); 8 ]" v3 n$ {* Q
8 m; e8 x, S9 l3 A; m& E# a5 L0 v- $dbhost = 'localhost:3306'; // mysql服务器主机地址
7 {, K! U4 p9 u$ {1 K, r& Q - $dbuser = 'root'; // mysql用户名
1 |! {4 B$ B( E9 J4 j( p/ m - $dbpass = 'root'; // mysql用户名密码
& Y7 m3 [, k6 T8 d8 W - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
% D& k2 o" Q/ p9 k1 H - if(! $conn )
. t5 n% _/ J8 v8 q3 Q" N, n+ Z& \ - {
# L; | B( d3 Q$ S% ~ - die('连接失败: ' . mysqli_error($conn));
: S: @; m* C6 ^# O+ ]/ C7 h - }
# b$ ^3 N0 ~/ B9 c3 R, ~7 m! G - // 设置编码,防止中文乱码 N& h5 x6 C" J- {, w( L
- mysqli_query($conn , "set names utf8");$ r2 n& k2 z6 f. a: e8 ~
- mysqli_select_db( $conn, 'temp' ); ; o: Y" g2 Y( ~; B/ s3 s3 w
- + {0 N& |5 ?% {1 N& u$ g
- # php中的文件锁 - V2 P- v( L# l& M: o' r& J
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
q" h) g j$ d) ]0 ?8 u - flock($fp, LOCK_EX);// 排他锁 7 J! i4 Q' v* L0 I7 O- G6 t
) ?, i- h( k* F: |" Z9 ?- $retval = mysqli_query($conn ,'SELECT id FROM ta');
" a& d+ ~0 d1 _& b - while($row = mysqli_fetch_assoc($retval))7 W5 L% A) ^% E( Z1 ]- ^ y" h- ^
- {+ l$ Q; h/ n- r1 V: X
- $id = $row['id'];8 F% i# T2 t. Y# `' ?
- }9 D- q1 I' v/ T7 i8 c3 `- {) h
-
1 {9 `7 B8 b7 B - if($id > 0) , x/ U9 l. n9 E+ T5 `* w
- { : m% S. B9 n% E- @8 w' @3 j
- --$id;
, b9 F# z$ Q [6 ] - mysqli_query($conn ,'UPDATE ta SET id='.$id);
% Q# x+ |: f$ _0 Z) _ - } . H3 |5 g) Y2 w, Y- @8 o: d
- # php的文件锁,释放锁
% s( E ~ b* _3 g. W - flock($fp, LOCK_UN);
$ P' g4 U3 N# v' P5 J& ~( _ - fclose($fp);
( {# Z5 w0 d. ~, w* V
9 s2 @7 ]1 w- [- r- // $res = mysqli_query($conn , 'SELECT id FROM ta');
+ l- ^0 t4 s/ }; H6 U# o) g - // while($row = mysqli_fetch_assoc($res))
# e* r7 R9 o" O- o0 f - // {0 B+ ~6 K0 C5 p4 U7 u9 u6 d
- // $id = $row['id'];
5 t/ T) J: G4 @- s0 {. S3 ^ - // }
' F- Z1 h: k% O - // echo $id;
复制代码
) N' H0 k# K/ l8 h; I8 n
* e8 w7 U7 X- D( Z- b9 T抢券活动实例: - public function envelopeSnatching(){
- x1 r9 d' }$ Z9 A; Q7 c - $lingqu = $_POST['type'];; g3 m8 J6 ]$ w) n- H) Z
- $uid=session('u_id');//用户id
& X9 B2 a- D3 f; u" v: | - if(!$uid){4 X2 `7 F7 b; y4 G8 P4 K. ?
- $data['msg']='您没登录,请先登录!';
/ s, Q1 w- U8 T( j( e5 C - }else if(date('Y-m-d') != '2017-12-12'){
5 S0 ~+ ?1 ]( ?; P - $data['msg']='不在活动时间内!';
) X9 x7 M6 W% e- m' r b: b - }else{
7 W, v+ G7 E! m$ e; I' Y - $hours=date('H');//当前小时数
/ `' @' x9 H n& D; \ - if($hours > '09' || $hours > '17'){
% z; ^6 E+ U0 s* n5 o0 s2 @ - 8 Q/ [3 e5 g9 ~! \
- if($lingqu == 1 || $lingqu ==2){//点击10点的
, l: K: L9 h; Z4 t! Y" L' i7 d; U3 }6 I - if($lingqu == 1){
8 B/ d1 U/ d1 c! e! l - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
3 e: z8 A! }! B" _: ]" Z - if($hours > '09'){4 t! `$ \4 V0 F
- $num=mt_rand(25,28);//优惠券金额
1 V4 |, m( K5 Q8 G- a# C - $id=1;/ F) }& S( L+ ?. J
- }! Z& z% L: Q% @- q) p' f0 Y$ w5 y+ x9 @
- }else if($lingqu == 2){
. g& L" V, ?+ G" u- k5 X( ~ - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();% z* x+ _; k' d. h
- if($hours > '17'){8 z( ?) z5 A- O- r" U5 @
- $num=mt_rand(50,55);//优惠券金额/ c8 w. N0 _7 n0 |
- $id=2;9 ?: J# ?2 O* \9 d2 H L: d
- }8 n5 k, J! Q$ I V' c
- }* J0 c( \% G2 P' l, L
- if(!$id){
( V9 y6 j- W8 A# W - $data['msg']='时间还没到,晚点再来吧。';
% \" }: | X. U7 e - }else{
$ Q: x W7 B" {" m. T - if($is_lingqu){2 z0 h: B3 D5 |7 v& D/ e2 t
- $data['msg']='你已经领取过了,留点给别人吧!';/ [8 Y. h) @$ B( k! W3 ]
- }else{
5 N$ p" k8 P2 R. M8 | - //锁表
9 o: }3 S$ P4 ~ - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
. U+ S7 Y7 f* } - $active=M('active_num')->where(array('id'=>$id))->find();- r4 i: n6 u" M" ^5 V y: h9 X A9 |
- if($active > 0){
, D" j, K- w! p5 K - //开启事务" i& z2 o1 W9 ~& r/ G2 G! G6 ]
- M()->execute('start transaction');
8 R; w" }& r" j - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));; O9 C- ?& A1 D# r; r% G+ ?4 `
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));2 W1 H" S! M/ B' Z0 Q* c7 ~
- $members_preferential = M('members_preferential');
7 Q% C9 X9 R4 A, N$ b* p - //对应投资金额,
% G% T2 [( f: E% J" i - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));7 S7 [& h7 D& I5 r! r4 n' C
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');) d4 _: D2 i! Z2 [
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
% v" n3 ^% s# Y" r# ^* | - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
! f T: m! a- T V( P" u - if($save && $add && $add2){3 c7 S2 ]+ b y5 E" R" }
- //事务提交. O3 f5 V# |7 i
- M()->execute('commit');
3 c @2 o9 E5 z4 A- N - $data['msg']='恭喜你领取了'.$num.'元优惠券';; | m) [; Q' C( Q
- }else{
: M! x$ t4 _ R% z. H0 X) p - //回滚5 |$ K/ o& @! ?& h3 H" B
- M()->execute('rollback');! x7 ^) d& L$ y- j
- $data['msg']='未知错误!';
# x+ I1 O7 y, D9 u! ` - }6 `% Z6 ~- O" n$ d3 g) X
- }else{0 ?" |4 Q' j7 x% t
- $data['msg']='红包已领完,你来晚了!';* v: n$ _8 l7 z7 o% a* ~
- }4 r9 F, L0 R/ p$ \" R
- M()->execute('UNLOCK TABLES');
* n3 o2 u! \/ M - }- U. O1 y/ N& L& k2 l
- }
/ _8 ^& g; c: k - }else{' g/ _& h- T& }6 ^$ a
- $data['msg']='非法操作!';: {: O% L! k( w% g
- }
. `1 Y2 K; J2 X5 m) b - }else{
0 c. c. u# @+ N/ U: v - $data['msg']='还没有到活动时间,请晚点再来哟!!';
: n$ C# F+ p, ~- l( w: v- G: s - }5 h Q8 v, G# H# B; f, T
- }4 E$ j5 a- R" \
- exit(json_encode($data));
8 {5 E* \: B/ L4 @$ }* V9 ?. g - }
复制代码
( r# z9 I: W" I# T3 F" g: Q' M! r* O6 T4 U- |4 c f
|