模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
4 y% a) T: c z+ b3 T+ VMysql中的锁语法:
+ _5 x9 i) _6 M3 a7 E5 zLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
4 I' O, }" W# n3 U/ TUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
9 @# o/ P- d N/ @4 x% sWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞" f5 a( H. x i- f
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
: g2 I9 H& X/ `文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
/ P2 V4 b3 @; ~2 R4 s' _9 D% `测试时,有个文件就行,叫什么名无所谓 总结:
" B1 V6 \ P$ H3 B项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:+ \9 a" _1 A1 }1 V! M# S# Z z6 K
1. 高并发下单时,减库存量时要加锁/ _2 r3 O' H7 D' d Z
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*, U+ }0 V. ` r
- 模拟秒杀活动-- 商品100件
" v* z3 {3 @& O# y; V - CREATE TABLE ta, @1 ], O4 o1 Z( h) e
- (! ?# R' G( F0 `+ J% f$ q9 l
- id int comment '模拟100件活动商品的数量'
3 B! u! _% O8 v' {! T: O - );! D/ Q8 f/ |4 d' J. C
- INSERT INTO ta VALUES(100);) e- F3 [/ p' w' g: E
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件$ V7 {' j9 r5 P
- */
) T, g2 W8 _. _/ `' u- G4 e( ^4 G4 X -
/ G1 U0 Z7 P( n g% } - // 关闭错误报告
5 S6 y; a/ m( a - error_reporting(0); $ Y, A8 n& N2 m8 [3 u: X
# |2 _5 J' C l& y4 Z3 Z- $dbhost = 'localhost:3306'; // mysql服务器主机地址8 i& s' x/ K# F8 l. s$ B
- $dbuser = 'root'; // mysql用户名% h8 c- E9 G9 F
- $dbpass = 'root'; // mysql用户名密码
/ v# m5 n# w) ]$ s2 H" P - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);- U2 N& ?6 K8 |& q
- if(! $conn )
8 P- I+ ^: k6 V' j, o1 t7 \3 F+ m4 ?" r - {( |9 ?. Y) v+ Q8 N; @
- die('连接失败: ' . mysqli_error($conn));
, [) U5 M) U- u; C, s - }
5 W; K" W, V& u+ S3 f2 q - // 设置编码,防止中文乱码
1 A6 m3 M! v; O) C f - mysqli_query($conn , "set names utf8");
2 @0 O' V2 P! r5 S$ Z# X - mysqli_select_db( $conn, 'temp' );
! w& \/ m* Z/ d- x4 b: e$ L
( M- [% m/ T+ R7 x- # mysql 锁 7 }& f+ S) c! c$ t- w/ S
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
" R- |, X; a0 v* b - $rs = mysqli_query($conn , 'SELECT id FROM a');
4 O0 r, ^- e. S) ^% \' { - $id = mysqli_result($rs, 0, 0); : n7 H( K5 @' f: s0 M- T' I* ^
- if($id > 0)
3 o% W. ^' S. `0 Y - { 8 X& q, _( W. c N8 `
- --$id; 1 ]$ z: O5 r$ ?0 V
- mysqli_query($conn , 'UPDATE a SET id='.$id); / j5 P; k. k/ n/ y" y
- } ! \! y) e* V3 C8 F0 U
- 4 Z/ k4 Y6 ^/ c7 ^ ], N8 I
- # mysql 解锁
* ^$ Q8 T! ^" H2 Y - mysqli_query($conn , 'UNLOCK TABLES');, v2 F x% U1 ^. k% m
- //查询解锁后的id值 v0 V+ L0 ?8 `0 s
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
; I; L7 ]# b1 g - // while($row = mysqli_fetch_assoc($res))
B7 u c7 t' z4 q8 z( h. \ W - // {+ M& N, C7 f( t
- // $id = $row['id'];- r( b# q2 ]( A! D
- // }
) B: V3 ?; D) B0 s - // echo $id;
复制代码 5 F6 ] f9 S) f
2 x5 k' w- X* _8 E/ u/ a
. [1 z; X/ a: q/ ?& @( n% SPHP文件锁示例: - /*4 {4 z! Q& |, T- \9 b
- 模拟秒杀活动-- 商品100件
, m1 b, I6 i3 U* ]6 j - CREATE TABLE ta
6 q j/ s* o1 a9 e - (
' \7 [9 {5 K, V, X- l: N - id int comment '模拟100件活动商品的数量'" f5 @ D Z% O) d( n% q4 S9 d
- ); T% ]* [: F) x2 x) [ H* ^$ G
- INSERT INTO ta VALUES(100);7 d) A- M1 U! I) A4 t& Q7 M
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
' R `- k4 M& a3 P2 w/ P3 K - */ $ \- L H; u$ Z+ d$ A* x
- + S, |# G) F0 n% _; D! A- Y
- // 关闭错误报告
' h/ b" E. T$ k0 M, p - error_reporting(0); 8 B ]. i% M+ Y# z
- * Z" X3 v$ U' }1 Y- D
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
7 G# z* f; N+ k, v( [ - $dbuser = 'root'; // mysql用户名
$ @: N0 [. h% J' Y- b% o - $dbpass = 'root'; // mysql用户名密码+ v% x/ h" f/ @4 t- }0 U
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);( q& y# n( o: m8 v7 p) X
- if(! $conn )
& h1 a7 Z. j; i0 w - {
8 I1 e0 }* z5 v7 }+ @ - die('连接失败: ' . mysqli_error($conn));) U) A$ h; I- S1 l+ v; }+ S. ]4 D
- }4 v% H7 D5 G) Z1 c1 e, P
- // 设置编码,防止中文乱码) e- D6 O& j/ C m3 c7 K5 H
- mysqli_query($conn , "set names utf8");
* _' f9 S2 @( g0 P- n - mysqli_select_db( $conn, 'temp' ); & k/ G) y/ e% u; h% E$ I# k2 g# L, @3 e
5 v5 e7 U B4 t2 Z- # php中的文件锁 7 P/ ?; ^! `, }$ b9 x' |# T& X; E) A
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 7 G3 Z6 C& N$ S0 ?6 b" x# k; X
- flock($fp, LOCK_EX);// 排他锁 * \' l: s3 P6 ]3 a' o
6 t4 S( ^& A+ W% @* ^- $retval = mysqli_query($conn ,'SELECT id FROM ta'); . D/ d0 j, s0 N K0 @0 C) s
- while($row = mysqli_fetch_assoc($retval))
: `) R z6 o, Q4 k3 E% P7 B2 {- v - {. r$ I& l3 L ^9 L8 Y
- $id = $row['id'];$ n& Z) d" x8 G0 {6 W
- }7 h7 u f2 I( a
- / e( J0 L4 r8 X' {/ C
- if($id > 0) , r5 v# v1 f" J) y' |, t
- {
: ?% h. J% K3 q: u" P) ~ - --$id; * G9 o4 ^4 a* H; W0 V1 r/ D
- mysqli_query($conn ,'UPDATE ta SET id='.$id); 3 ~5 x' d. S$ w+ K5 ?0 Y1 ?* g
- }
. T9 \( K A3 F# ~: ?* u( k - # php的文件锁,释放锁 * {. E2 u$ h9 s- u* U- X
- flock($fp, LOCK_UN);
- v& w/ ?3 G3 {. ~2 O' a! j - fclose($fp);
. i% I# `5 Z0 F }8 h/ Q; g - 4 |, |$ E# N% e& v' U
- // $res = mysqli_query($conn , 'SELECT id FROM ta');& e4 u, G7 Q# o7 J1 H1 i
- // while($row = mysqli_fetch_assoc($res)): h) s! b8 X0 L# b
- // {
* g$ B* `; Q: U - // $id = $row['id'];3 ?$ y! P9 a' T5 x5 `
- // }
7 o7 \4 h8 N9 `2 P - // echo $id;
复制代码 0 b* i* n, e' P! I4 k8 H
0 c# j) _1 M1 E+ M抢券活动实例: - public function envelopeSnatching(){$ ?- M) L' W' |, c' l$ {* X
- $lingqu = $_POST['type'];) Q' f. Y4 h8 |; @& ^" d. R7 l1 e d
- $uid=session('u_id');//用户id
" K* m" [" e4 K - if(!$uid){
" K# s2 A4 r/ F. I8 J# E0 H9 S - $data['msg']='您没登录,请先登录!';5 c, q6 `! g, G/ M9 Z
- }else if(date('Y-m-d') != '2017-12-12'){1 }$ \( }5 ~- G4 ^
- $data['msg']='不在活动时间内!';
! M, }' G& P. r& C3 o7 V - }else{
6 @. o7 j3 T. o - $hours=date('H');//当前小时数& o- `7 y k1 p9 V4 q( T" @4 @
- if($hours > '09' || $hours > '17'){( ~6 H) {; q! Y. ?
" J3 _- ]0 r/ l8 N, Z- if($lingqu == 1 || $lingqu ==2){//点击10点的
5 Y+ g9 L0 g3 i9 W' T - if($lingqu == 1){' m; c6 o) \# L4 J" z
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
6 t R& M, }" i+ Y9 o! N - if($hours > '09'){
' P' d7 J5 b4 {+ }, F% X# R - $num=mt_rand(25,28);//优惠券金额
. R* c9 t) c4 s6 f) O - $id=1;# C M# t& ^: e8 X0 O/ S4 C& U& s
- }
% l, s% s* y2 H) u5 H$ K0 r9 }6 B - }else if($lingqu == 2){
) G, j+ A5 A* h, n7 ?. R4 W8 v) c - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();' q4 p- }6 q( i' q$ x- Z
- if($hours > '17'){9 ~; v' x, H% R/ {5 e" \# ^
- $num=mt_rand(50,55);//优惠券金额- ~6 K' R5 l1 u0 G/ x
- $id=2;
8 f$ y2 N! C5 { - }
+ o: \# t2 K% U0 C6 m - }# T3 l* M5 Z$ P( K
- if(!$id){: \( @4 x9 |2 N) v
- $data['msg']='时间还没到,晚点再来吧。';! ]. w e! Y1 Q5 [" r$ X3 Y# T) I
- }else{6 g7 U2 z2 L) T
- if($is_lingqu){. S8 q. ]( \9 M1 v$ a% ]
- $data['msg']='你已经领取过了,留点给别人吧!';4 v# e* F, U; B( ^! t
- }else{) [- ^* S( _3 [- ?6 c! W! X
- //锁表5 n# ]5 t/ ~ [% I% n
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');: v: [6 }% P" B" o; @
- $active=M('active_num')->where(array('id'=>$id))->find();
! s& H) K# s! ]% g - if($active > 0){
# r- O; X- x, t# W) u) \8 I - //开启事务
7 t2 ?# m' ]9 F6 @8 j* d; J - M()->execute('start transaction');; @3 E# e; H9 G5 {9 I7 S
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));6 w: F4 T# w+ P5 f- A$ _' H
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
$ ~; G4 Z# y2 l: z3 Z - $members_preferential = M('members_preferential');
) z, F6 _; B4 l6 d; i - //对应投资金额,
/ s* h" ~" T3 N" n5 Z - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));4 ~: u: w6 Y3 s4 a7 W$ i
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
# G. _: b0 n! z - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
0 ~) C* C7 K1 U( P - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券9 `/ A% Y) g# {* z( L
- if($save && $add && $add2){0 ]0 z. z( ~1 m7 L# y, v$ ~4 U
- //事务提交
& V. b* Q/ u9 N( o - M()->execute('commit');
7 c* Q8 x8 }& I1 i1 x3 Z - $data['msg']='恭喜你领取了'.$num.'元优惠券';
. d; x' \! m! O - }else{
& c9 h& m: \7 y: m m - //回滚
: h2 e$ A! H$ j4 b0 `: V7 A( g3 N" ^ - M()->execute('rollback');$ |( ~& \; w' I4 e1 ?
- $data['msg']='未知错误!';
) }, {4 j: v0 S/ h- i - }
' u1 T2 r, T/ ?: K9 j; g - }else{! T a' M8 |7 J8 \
- $data['msg']='红包已领完,你来晚了!';3 @9 ?" N5 y' {- t6 Y: ]
- }
4 z' c1 M7 p+ b! y3 f - M()->execute('UNLOCK TABLES');
4 P/ }6 T$ z2 s( V$ u5 J - }
% \2 B, C* i) z1 v9 ^3 ` - }+ j$ a7 v: a: \9 L
- }else{
0 G& E( F% F( O7 I/ @: p8 z - $data['msg']='非法操作!';
n6 B3 ]9 X5 P0 D2 P4 {. G2 S4 [4 V - }1 k/ x3 [, J- _2 P: H
- }else{1 }- |! y7 n/ _" W: |
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
: J0 B2 x) e% o% T1 {+ _ - }$ E4 `$ o" J0 _
- }; B ]* _2 H' O8 ?, D) J
- exit(json_encode($data));6 K# g. O) s) \3 l+ O! ~
- }
复制代码
( J& l, X! H- w4 M: c6 z p
+ R2 i2 P& D0 \1 q& Q# ~ |