|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 6 l$ J; n" u5 \4 x
Mysql中的锁语法:
: g. R/ k5 [/ ~5 O& gLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
* k/ C- F( ?% H/ s0 ~5 M7 ]: A: h$ AUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表+ R7 g# U5 p: ~) h$ O# i
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞' G' _ Q9 n# t {9 I8 ?
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :+ m8 ^9 j1 c' D3 `$ T% c
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
, Z3 ]7 H' ]" t/ u+ |# h! ~测试时,有个文件就行,叫什么名无所谓 总结:
% b7 U/ O9 D. U' _# Y5 r项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
# @% V- t0 a8 s- M. ~& ^% H$ j1. 高并发下单时,减库存量时要加锁
9 L$ X6 r( n3 F+ `2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*+ q |$ ]# v- I1 M. z) R
- 模拟秒杀活动-- 商品100件
" I4 F: B; D# D T2 A& S - CREATE TABLE ta
; z$ h- b3 i# j) j: ` - (, c7 C8 @, m8 d' t9 o+ V h
- id int comment '模拟100件活动商品的数量'
" e7 \, i! r9 W. M% N+ Q3 O( S$ S. T - );! d0 p/ @# ^8 u7 A# t' q/ T/ E( v& V
- INSERT INTO ta VALUES(100);
' p4 F$ r) j& Q1 @2 o- N- B# X - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
9 B- \& |% L5 s% c) I( a - */
$ C6 p' Y2 C& E -
7 m5 O( Q3 Q# k: _ - // 关闭错误报告9 j3 F2 w* `) L% w, J
- error_reporting(0); 5 ^6 A4 p( ]. e) ?: ~. \
( }* U, p7 C0 f- $dbhost = 'localhost:3306'; // mysql服务器主机地址6 N8 o. Y; b \; h3 h. u
- $dbuser = 'root'; // mysql用户名
/ |+ C; E7 S7 E3 `! w - $dbpass = 'root'; // mysql用户名密码
3 |3 {4 K7 B& E - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);- l: x( U' U" F. B! I$ d' A6 m# q
- if(! $conn )8 `( c, }3 ]% e, i
- {$ a4 h$ i1 V2 F. l) H
- die('连接失败: ' . mysqli_error($conn));
8 [2 R5 \$ m# K, z8 r4 O5 i - }3 n) J' ? S( t& Z+ w, ?0 ?- i$ X1 x
- // 设置编码,防止中文乱码" U6 F5 |- ]! Q) E
- mysqli_query($conn , "set names utf8");
7 b+ H5 ^; ~2 r0 ` - mysqli_select_db( $conn, 'temp' );
3 W% Q0 G# D- n; K, q - ; l- G- d7 Z: l& L: U: A
- # mysql 锁 5 g$ Z! R1 T3 \7 ?" w, }! B2 q) [: ~
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 ( y9 Y+ c1 ^" I( C) k* H5 x) N! K
- $rs = mysqli_query($conn , 'SELECT id FROM a'); ) c" s3 M& s* j: L4 f+ g3 \
- $id = mysqli_result($rs, 0, 0); 6 s& i. F3 x) d& z' O+ d
- if($id > 0) ! `) S6 q; @ r, \: Q9 D# s! J
- { $ m$ k# Q5 }$ ]
- --$id;
+ l+ S$ n& c& D4 B' E3 R# t - mysqli_query($conn , 'UPDATE a SET id='.$id); 3 C$ p3 ?8 @) N8 n
- }
' T( b. f' j3 U, t+ r3 k) T; H+ n
' j0 v6 Y" Q8 S! {: J5 m1 |& ]2 S r- # mysql 解锁
! x9 O/ w% w' p7 q. @ - mysqli_query($conn , 'UNLOCK TABLES'); m T) D3 i2 j9 a% T! e% B
- //查询解锁后的id值' T1 H2 a8 ]1 g/ ~) w" A; [
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
( \4 Y) y3 ~, T' x( X* k, t& O. C/ U - // while($row = mysqli_fetch_assoc($res))1 q( l8 u" A" p
- // {5 H0 H4 _- Q/ ^: k. J
- // $id = $row['id'];
0 @, [5 e5 ^5 W; V" r" @& f+ C - // }- ]2 j* B+ _* N. r$ S/ |4 \# H
- // echo $id;
复制代码
. `* H0 Z ]: ]9 h7 }( `# E, T4 y t4 m3 u* z1 h" r# i5 e
% l% y% z) K7 }* f9 H
PHP文件锁示例: - /*
8 Q1 |+ ], g3 }- t" x$ D - 模拟秒杀活动-- 商品100件7 ^; L5 I+ E2 K c1 I
- CREATE TABLE ta
/ s! Y0 m% d" l" I! O# g - (
, o- F% l: `1 a; f1 l - id int comment '模拟100件活动商品的数量'* N" p( s/ z N& c1 j
- );
1 N- x0 }( P5 G# I - INSERT INTO ta VALUES(100);0 J; t: u) D2 g2 P# R7 {
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
9 K* _6 x& _1 y+ {/ U8 x - */ % A; ~. ]6 e: b* y
-
. x: ^% I. @3 r3 h# X! U - // 关闭错误报告0 `5 y: X1 {& _. H& j
- error_reporting(0);
9 W) R$ i4 |8 b2 Y - ! E' ^! @: W0 T( Z3 h7 m' v
- $dbhost = 'localhost:3306'; // mysql服务器主机地址; m% K& x4 M$ i* L# a. q
- $dbuser = 'root'; // mysql用户名
' ~" h3 m* W Y' X0 ~5 a - $dbpass = 'root'; // mysql用户名密码
# o& j: Y2 O, ^# H/ X: t - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
9 E9 ^4 G/ O: ~ - if(! $conn )
, }3 g4 d& _( v: T B* o5 B - {8 _( B' F; t& a+ l k, s2 m
- die('连接失败: ' . mysqli_error($conn));
$ j0 i3 W q5 s/ s. p - }" t3 a/ \5 F+ J) H C$ e* k+ B
- // 设置编码,防止中文乱码$ ?. d4 y, G+ E M% k, T; x; n
- mysqli_query($conn , "set names utf8");( a8 p) o( F3 t9 n: @5 a6 n2 h
- mysqli_select_db( $conn, 'temp' ); / I; Z& a9 u8 }6 c" m6 X* l
3 v+ x# q% I# b1 S/ ]) i% `5 I- # php中的文件锁
4 p2 j" H+ z$ r - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
* ]1 ?3 H) [1 ]& N! _. ?+ \% l - flock($fp, LOCK_EX);// 排他锁
" ]! ~% O) r0 ?4 j$ N0 Z - 9 A( O3 F( o+ t8 _2 y6 g( I
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
+ V$ Q5 n4 a- n1 F/ @ - while($row = mysqli_fetch_assoc($retval))
5 i# w8 B7 h" m - {
( _; P# ?! B, d8 e4 r6 ^) k8 R - $id = $row['id'];
' _- m, h d9 \9 y) y2 _ - }! r$ c/ m x: c$ f5 ?
-
( W" F& X3 N3 ^$ p' s- p8 l/ { - if($id > 0) 2 p% ^1 S+ U- ^; {0 x
- { 9 l k: P2 t7 d V
- --$id;
6 p- m" d+ j, i1 F9 M - mysqli_query($conn ,'UPDATE ta SET id='.$id);
) J5 M# M6 {/ @. b4 Q' J - } 4 T- ^7 \) n! _! Y
- # php的文件锁,释放锁
) ^2 {4 A9 F/ C" h4 @ - flock($fp, LOCK_UN); k* k7 H, h+ W: M( i2 J, \3 H0 j
- fclose($fp);5 O; g* ~8 ]# o: M0 z
. _6 V2 j, Q8 _8 H- // $res = mysqli_query($conn , 'SELECT id FROM ta');4 B: C: j; k; _8 ]
- // while($row = mysqli_fetch_assoc($res))! V/ w8 S8 L: u" e% V% F+ i$ Q
- // {6 y; E9 t1 T' q8 M" |
- // $id = $row['id']; ]; R" P1 T8 ~
- // } [* q9 l" }9 f7 U
- // echo $id;
复制代码 * v k7 e9 [7 t# d& X6 f q' D* f& ~
+ @# H, O5 w b
抢券活动实例: - public function envelopeSnatching(){
( O( c- r7 {/ @! [5 I - $lingqu = $_POST['type'];
; o/ T: y4 G! D; t# e - $uid=session('u_id');//用户id6 C( l( H+ o9 I" _( s3 y
- if(!$uid){
! C2 d, b/ ?( `# F- N4 m5 ^$ q' g Q - $data['msg']='您没登录,请先登录!';* g* _5 n$ r: a0 r2 E3 X( G8 m7 e: D( p I$ ?
- }else if(date('Y-m-d') != '2017-12-12'){+ f6 ^9 J& R6 J# o' }
- $data['msg']='不在活动时间内!';
, f+ o' q/ R0 I/ x/ Q% a - }else{& R/ s( g- s5 n. }! o
- $hours=date('H');//当前小时数
6 d C. A! o( U' t: p4 ^) F7 p. R - if($hours > '09' || $hours > '17'){- `9 M B% ]1 x7 q4 A
- # j3 i9 j8 {, d& l7 u) j% F
- if($lingqu == 1 || $lingqu ==2){//点击10点的
4 o1 ~( _* V( B! I& ~6 n) ^ - if($lingqu == 1){
9 _- w% g! i) U7 G* v - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
3 q' k( f$ Q6 @5 Y5 q* k - if($hours > '09'){
7 q$ @* a- w, Z- D, g" d( v5 N - $num=mt_rand(25,28);//优惠券金额- A$ U/ [8 y$ F' K( P
- $id=1;
6 a- e( H$ y) o# ?& O! e1 k7 J% [0 D - }3 s5 Q q$ [; E( F5 x k
- }else if($lingqu == 2){+ I+ ~" L( }0 \3 T8 ]. u5 K; `0 x3 ~
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
s$ \/ R3 \! J$ m3 p - if($hours > '17'){
7 A- x l4 H( t* l: U7 @6 | - $num=mt_rand(50,55);//优惠券金额0 S5 c6 A. S# t, T2 Q
- $id=2;9 n) b$ h6 O7 P4 H7 S5 @7 I% ?4 u
- }
7 k# r1 q! U( n - }( v, x: z8 \* T- s, y: x8 w, ]# ]
- if(!$id){
$ |! R) |+ ^" @9 n1 m$ o) A3 H1 B5 A" I - $data['msg']='时间还没到,晚点再来吧。';
+ V& Q+ G7 y* k6 U: e" F - }else{' ]+ `% d( L' I2 x' @
- if($is_lingqu){- D2 N3 X! h; O
- $data['msg']='你已经领取过了,留点给别人吧!';- |1 R9 r' v% _
- }else{6 r$ j, ]5 ~3 L& B1 D
- //锁表
8 e# M7 { Q) r% O- P. i- x - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');( h4 o k& x. r# O; k9 G6 P
- $active=M('active_num')->where(array('id'=>$id))->find();* O4 v8 {: I: M( Y- u
- if($active > 0){
1 B0 O/ D) H2 j7 [ - //开启事务8 G/ F5 v9 P6 X, r
- M()->execute('start transaction');0 m: [+ d4 y1 j1 D
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
5 L( U/ {. A5 ~% o7 X& O7 G - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));( I7 K! F* ~6 p
- $members_preferential = M('members_preferential');! q0 \% p0 g) ]) O0 A) V* ?) r
- //对应投资金额,3 e! B8 O+ b; @6 L! i+ u5 Q% m
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));1 c9 \5 K* Y9 h, s. s8 A9 ]6 H
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');( w5 I# h' v) n4 {& F0 R- A/ I
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
5 g& n+ n. {! C* @ - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券. ^: w, h$ J+ X3 t( L
- if($save && $add && $add2){3 {2 X* O2 e8 `/ i5 g' i, k3 r2 Z
- //事务提交
+ x/ E* S# b# X# W/ O @1 V - M()->execute('commit');
4 J" D( q: a) L* [0 B( |0 S( A* F - $data['msg']='恭喜你领取了'.$num.'元优惠券';
+ E( b4 Z* }4 a8 q8 e0 Q - }else{( a1 t, z% `- Q
- //回滚$ D& P* c' [* P: e( _# @
- M()->execute('rollback');
) h8 C* q, b: {; C/ n - $data['msg']='未知错误!';# v4 {6 M# T" _$ Z v" n
- }
- n# k$ q2 P. ? - }else{
: N( @& m# ~% Q& |; n - $data['msg']='红包已领完,你来晚了!';/ G2 h9 l3 ~3 z+ }
- }
2 o8 @+ c# t* z# H9 H7 ~ - M()->execute('UNLOCK TABLES');, w" g9 X2 Q/ Q, j( _3 x) f: C
- }7 F& r, y3 F! V% K+ g! _
- }. j) w* g1 G3 H/ x6 e
- }else{
. m5 ?6 J& Q/ H* x - $data['msg']='非法操作!';5 X4 g" O4 |; I/ ?1 @
- }: Q# {6 I. W# i% a' j& _: c7 Q2 S+ y
- }else{( W# y% f& ?3 t. l; ]5 c
- $data['msg']='还没有到活动时间,请晚点再来哟!!';3 `) ^8 [8 r+ r1 `9 p
- }2 { e. M" u' i$ a- P1 ~
- }
: N; Y5 n# m& s5 r1 H - exit(json_encode($data)); F8 R) X5 q$ L3 V
- }
复制代码 * \6 {1 _2 ^8 Q4 B- V' |5 A$ a
; m! P! r. t7 n |