|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 9 _, `$ |% W) g- P
Mysql中的锁语法:
- ?# ? U5 ?1 Y* r+ z1 XLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】* ^5 n& S% A9 J/ M% ]7 Z8 f, ~
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表( Z$ [3 y4 H) p/ q1 Q( R
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
$ Z9 \! W5 P5 D+ U" V% _* ?注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :/ a& s; G4 o' p8 L" B: t
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
l) D5 E8 ^* K$ Q" I1 T, l' S5 D测试时,有个文件就行,叫什么名无所谓 总结:" g6 W" r- x; g& S0 g. A5 |, q
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
- f% {' S1 I' |7 v' X9 ]+ K1. 高并发下单时,减库存量时要加锁
1 @0 [- w; C! \2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*6 U% ^) D( U/ R& ?
- 模拟秒杀活动-- 商品100件5 I1 M) R5 Y1 V1 Q% L* w; O h; C
- CREATE TABLE ta; s2 K: Q# }& O1 B; {' R0 x
- (
( B& P/ h* K- A, y0 ?% z, U - id int comment '模拟100件活动商品的数量'4 L3 d' n. q% B1 T
- );
8 k; p I$ V) g& l+ N - INSERT INTO ta VALUES(100);# [' G' V- B; Z
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
: a2 @& w, H* C$ ^4 w - */ ! u& X# A4 L# G8 R
-
9 p& k+ t$ M8 J+ ]4 S: E - // 关闭错误报告( e: ^- h6 c0 a/ q
- error_reporting(0); ' S- v6 `6 `) a2 y
/ x4 D4 Y# o- V, E8 F- $dbhost = 'localhost:3306'; // mysql服务器主机地址
6 l2 u6 Q1 O+ o o8 r - $dbuser = 'root'; // mysql用户名
, G& [3 ]0 z, l/ c0 ^: [ - $dbpass = 'root'; // mysql用户名密码
' } R7 o3 N$ ^- X2 l( C" ?) b# m - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
) V$ I' O- O l% z" \4 g n - if(! $conn )+ N+ G# F' q4 K B
- {
# I' Q; \! G) E6 V- \/ H. C |. g - die('连接失败: ' . mysqli_error($conn));. x7 c7 V/ E7 W( ?; U
- }" h3 K3 L" H+ \4 ~: d
- // 设置编码,防止中文乱码5 {; {& R( a9 J* e, {
- mysqli_query($conn , "set names utf8");2 P- U( U$ j7 T& z* A
- mysqli_select_db( $conn, 'temp' );
) S/ P7 B* C- l T3 [* b: t - 1 ?8 y: k+ u. Q, l; K$ F
- # mysql 锁
' n6 i) t$ y: [* r$ r% {8 R - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
4 H+ }$ l# J$ F2 r1 e- W, d4 _$ g - $rs = mysqli_query($conn , 'SELECT id FROM a');
8 L8 C, k; t0 T, v+ F- ` @ - $id = mysqli_result($rs, 0, 0); , d3 \) \( D1 D9 u# a
- if($id > 0)
8 a' @( T2 G' o' _" G - { ' e% e0 E D) V- F5 Y( x
- --$id;
2 i" e/ I8 R2 v) V* e( a. c - mysqli_query($conn , 'UPDATE a SET id='.$id); * Y. ]; h" C3 S# z( H$ m7 e
- } 9 C6 w) {* a. H! y$ q m. l( x
- % s3 R1 ]' V9 H. k, b" h9 _; A
- # mysql 解锁
- W9 z# N, Y+ _ - mysqli_query($conn , 'UNLOCK TABLES');! a$ D' K3 M' W& w* Y2 P: e( K
- //查询解锁后的id值4 y: E6 j# l) l9 C, Q
- // $res = mysqli_query($conn , 'SELECT id FROM ta');9 f$ e! B' K s* h" Y
- // while($row = mysqli_fetch_assoc($res))5 ?* ] B% H9 z$ Q; h2 h
- // { g! a H7 d" U Y/ P
- // $id = $row['id'];3 h K! Y. w* ]4 P. p/ l! c; r
- // }- l% z/ B) Y/ a: t* Q
- // echo $id;
复制代码 % t/ b; v d6 M' V7 t1 N8 V, F2 J
0 E4 |6 `/ G( g3 w; \( H- h
( A4 {# _4 Y# Q# |) N: iPHP文件锁示例: - /*0 {5 }' k) @3 S8 X8 L: @0 E+ y
- 模拟秒杀活动-- 商品100件
$ G- O O" D' J& `6 o2 L: F+ m - CREATE TABLE ta3 `* U+ C( R* w7 f; T" Y5 ^3 T
- (
; g3 w9 k& N; P) ? - id int comment '模拟100件活动商品的数量'( v6 M( m f% ?7 T1 z
- );
( i4 \2 S$ q+ S- v F! L* I - INSERT INTO ta VALUES(100);
+ {: }1 Y! I- p( J( p/ i* l8 s - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
2 t, |# o. K7 d0 J, e& u/ b. ?* o - */ 7 l7 [- l: G6 |1 `/ ?! b6 G8 V
- , i+ R: `* ~6 |' q; \9 G9 @
- // 关闭错误报告* N2 w+ h( A$ j1 l2 A3 N7 i
- error_reporting(0); d. g2 S+ _- ~8 z2 A
- 0 e8 N) S7 u/ m. q
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
) A3 [( T2 ^ T: \+ g - $dbuser = 'root'; // mysql用户名
' F: \6 I X0 K d - $dbpass = 'root'; // mysql用户名密码
: X1 m Y _; { - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);8 [' F9 T- Q* O1 `& ^
- if(! $conn )5 ~! z' m5 m; G- ]( A" S
- {
( D( j. o r" B" w' V3 O7 d - die('连接失败: ' . mysqli_error($conn));1 @* \, `' t% P, |. x) q
- }5 W# Z+ e7 P! [ Q4 N' a
- // 设置编码,防止中文乱码
! R0 i# N( K/ E" H) ]7 e; g) A - mysqli_query($conn , "set names utf8");% z" F" H& {( C: C
- mysqli_select_db( $conn, 'temp' ); t: H! k1 t# _" r8 y
- & U- m9 h& @) Z* ?: \$ H) I
- # php中的文件锁
0 w/ M5 F. G- p d( E - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
2 `1 I; N; {7 ~% a. ` - flock($fp, LOCK_EX);// 排他锁 ) G' L0 b2 p1 j
3 Q& V; y, z8 q! N. m) `- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 8 o$ P8 _: g: m. l% \2 ?
- while($row = mysqli_fetch_assoc($retval))# h6 V0 e9 N2 G" H3 C2 j% x- R
- {8 r( [6 U1 K B1 N- K7 u# S9 J9 d5 O
- $id = $row['id'];
! r5 k/ b4 v J! a; _ - }
9 ^3 R5 h% U1 h7 V* r/ E2 R -
+ x# B3 p' x. B' j1 e. E8 f; B( _$ A* D* ?" _ - if($id > 0) # A( [2 C% H( {+ ]
- {
, C ?$ @0 U. C3 ` s6 H3 J - --$id;
8 L' y! N; b) L3 i2 c - mysqli_query($conn ,'UPDATE ta SET id='.$id);
0 R& W1 ]+ n9 { - } 4 E& x/ e4 ]; X* L9 P
- # php的文件锁,释放锁
4 b3 x& Y8 s$ k, A - flock($fp, LOCK_UN); - W# A% }% r$ I. Q/ Z0 g
- fclose($fp);* {5 n# `% i/ z! J/ F
; Z/ I3 g. v) s- // $res = mysqli_query($conn , 'SELECT id FROM ta');/ y1 ?) Z: q8 ^4 O7 ]
- // while($row = mysqli_fetch_assoc($res))
1 T; `! G' Y. O' G4 ~5 v - // {$ u8 J$ j0 x: `2 F7 B& D
- // $id = $row['id'];$ J* K; j! i! l. Z! F
- // }& y6 j) ^" [5 K* O
- // echo $id;
复制代码
6 J; F+ m0 p3 Y" j7 B" _! ?4 K- H
; f8 D% Y" m x& l% C6 h抢券活动实例: - public function envelopeSnatching(){2 z* x$ ^6 N' k) U1 b
- $lingqu = $_POST['type'];& I: _* W6 M( ~1 P2 N4 w0 v* o7 K
- $uid=session('u_id');//用户id7 U( Q9 z$ u% U/ T6 E0 |. B& m( ~
- if(!$uid){
; B* V; {+ a4 z, U - $data['msg']='您没登录,请先登录!';
$ m9 ]. Y5 c2 i- p9 e! ^6 L - }else if(date('Y-m-d') != '2017-12-12'){
& I" B1 ^4 ` f( V+ l" e6 A - $data['msg']='不在活动时间内!';( o9 E' W+ S) E( K
- }else{
5 u4 ^, Q) J7 c - $hours=date('H');//当前小时数
" Z8 ~3 l, c; c0 w; [ - if($hours > '09' || $hours > '17'){
, i+ a! k/ f9 m7 p( Y" Z; E
3 w! b' r* A- D- n- if($lingqu == 1 || $lingqu ==2){//点击10点的- ?/ {' Z' m: w/ u
- if($lingqu == 1){3 y' f E: J9 q
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
1 B% y( U! S: B- b, @; q; o - if($hours > '09'){7 i9 ?9 u2 l0 |) p( _9 H% b
- $num=mt_rand(25,28);//优惠券金额, }. H: {! f% f0 E8 R( i
- $id=1;" Y) d6 X8 S2 L( `# t Z3 q
- }: [( G2 m( K9 v$ x
- }else if($lingqu == 2){
. K6 o4 [ S8 E - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();0 `% Y( B$ q# C4 u7 y
- if($hours > '17'){
' K7 e8 i0 _- s. n - $num=mt_rand(50,55);//优惠券金额, p8 _, b: K/ i3 M2 ^1 J
- $id=2;
$ H3 o7 M# N+ f2 A2 \) N) e# S' w - }# e9 @/ A7 ?2 p; k5 ^ l+ }: q
- }" n! W3 \9 D* E
- if(!$id){
" J+ K( H! d! H4 D5 S0 W - $data['msg']='时间还没到,晚点再来吧。';
d! {; h* O& v( I1 u; w - }else{
?! u9 Z! f9 i- [9 v8 v7 f - if($is_lingqu){
0 v' @( K* p0 t: M7 j - $data['msg']='你已经领取过了,留点给别人吧!';
1 R. P& c c Q% R - }else{ @: e+ T) p$ ^0 h2 e- ^2 e% m$ F
- //锁表
; \# Q/ b0 G5 X0 M1 g - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');. d0 m0 l$ C, g3 r0 c; i8 J; E" k
- $active=M('active_num')->where(array('id'=>$id))->find();% x& F. ]( z6 l7 ^' m& @# Q. U% ?
- if($active > 0){
# G: i6 p8 d/ L+ @2 T6 ` - //开启事务
: u& E, r9 e' `" ? - M()->execute('start transaction');% G3 ^) V9 p- U- ^
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));' f% v Z0 E( N9 _" I4 v1 } r
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));: o: t( O" I1 s3 P
- $members_preferential = M('members_preferential');" J0 O' ?! z- j, w: x7 \$ q% ]
- //对应投资金额,- E+ H. F9 W- i0 J; }
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));( p$ w) ]1 |$ `
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');" j& v6 e1 W; C7 E% X
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间) ?- K1 d) J$ b4 o0 b
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券6 Z- a: g- }( U& z2 z) d$ O
- if($save && $add && $add2){
8 O. @9 D! X% h* p9 n - //事务提交& d0 n5 f; K% L+ n
- M()->execute('commit');) A/ f. @* e4 U' P
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
$ ?4 n+ l3 }% r) X - }else{- T+ E4 O2 o* j* K" F& K
- //回滚1 ]/ E. C& n8 ]# T% J
- M()->execute('rollback');. z) d1 D2 W+ [2 D1 ]* ]. `
- $data['msg']='未知错误!';$ b& x6 i! u7 Y+ e. ]! W
- }
" u0 _# r3 M8 V& s2 s9 ? - }else{# v4 R+ J/ ?$ a& e! |% _( M) p% G
- $data['msg']='红包已领完,你来晚了!';
y1 i# J( X8 X K8 j0 f9 z; K2 N2 t - }
* i# V( a% p. Z$ A. Q5 R6 u; O - M()->execute('UNLOCK TABLES');7 j2 W5 J; C, V2 a8 ?7 f
- }0 A- B" P9 R; m* z5 c0 o* [
- }
7 Y8 m) v, R$ o6 Y( P$ B - }else{
v* Q9 o. a- o& h) o7 o5 j - $data['msg']='非法操作!';
6 Q- `; G; x5 U, J8 ?- j - }9 [* J" \" A- O0 k
- }else{" ^3 M( z8 z: M
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
- H3 [2 O. Y$ o% X - }
4 p0 b" e$ J0 r5 z: f - }
9 P5 F* S6 Y6 p; T7 } - exit(json_encode($data));
. R6 Y( J: v2 H" O% O! x& [' w - }
复制代码 5 b' J6 l. L! f" o+ t b
' Y9 ^0 i" V- W& Q L$ V* s |