|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 ' [" r3 D; t% Z( C, c r9 o: E
Mysql中的锁语法:' d! U3 R, P( {8 H# P, q8 i- ^
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
& k: L4 Z/ L9 j) `$ ?UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表5 H. L2 b: J. \
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞; N1 d! z% g8 ~- p! i" V3 v- S5 G9 k
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :* |$ u L0 r$ q1 e' `
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
_% N9 e& M' [; {4 R测试时,有个文件就行,叫什么名无所谓 总结:3 P" M4 Z/ B; ]& k" _6 S: B
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:1 @4 o _, P- W/ b9 Z5 N- P
1. 高并发下单时,减库存量时要加锁
$ A1 p6 S1 _$ x2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
5 _9 y0 q. Y4 g" G - 模拟秒杀活动-- 商品100件( j- n2 U# _6 C0 G( n
- CREATE TABLE ta; m9 c& {8 ] u; R; B) t( l$ J: T9 f+ L
- (
" j! ]& E7 ?/ t - id int comment '模拟100件活动商品的数量'( \; ^6 h% v4 I! M: c$ Y) u0 E. `
- );
3 [* N0 H% p6 m9 a1 x - INSERT INTO ta VALUES(100);
1 B, \5 o. @7 P) k - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
8 f6 x* N2 K+ }, t/ U# R - */
7 @6 }6 r4 d" ^% O - 5 V6 ^2 l8 A5 y l: L O1 n
- // 关闭错误报告
+ q l8 _2 m5 \% F2 y! t8 V; X - error_reporting(0); 4 ?+ G: Y& v+ h/ ]: i
- . J- c- [. ~4 D: Y
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
& ^) x/ p* G/ ~1 a/ A4 F7 c) i - $dbuser = 'root'; // mysql用户名 b* ]/ u. F) k# S1 h% [# p
- $dbpass = 'root'; // mysql用户名密码* ]5 g9 h0 E4 g! U/ {
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);) i' T( ?0 ?2 [$ i2 W" l$ b4 D* I
- if(! $conn )$ G9 P! U' a$ m. J2 l- y2 Q
- {0 i! \2 p7 q4 Q9 F$ z ?3 I
- die('连接失败: ' . mysqli_error($conn));
2 t1 R* ]8 E( F2 _' s - }! L; S( s& R/ q2 ?
- // 设置编码,防止中文乱码& [+ ]# F- e% ^$ Q) \
- mysqli_query($conn , "set names utf8");! _# u9 i" B* e5 t; `( u& `
- mysqli_select_db( $conn, 'temp' );
5 g( k+ E; @' E1 [- d5 }( F8 p. _
$ a+ _9 j1 i* z+ Y, X) K- # mysql 锁 % ?. A$ q% d% Q7 {
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
. @+ K! Q* d, c# D, J - $rs = mysqli_query($conn , 'SELECT id FROM a'); . ]: ~- Y# L9 U
- $id = mysqli_result($rs, 0, 0); . ^* ?" S3 ^# P0 {* _1 N
- if($id > 0)
/ b+ U; T+ e' U0 [. \3 f% `$ @1 ` - { 1 m& x% m+ T' g, k7 L8 [" i
- --$id;
7 H: j( \' K7 l6 T - mysqli_query($conn , 'UPDATE a SET id='.$id); & U. w+ n" \% p1 |' F) A# r/ T
- }
' S0 X; ^+ q, n2 G6 d - ) k8 H6 }, `' [6 x
- # mysql 解锁
J& S9 C. c3 O. ~% q3 t - mysqli_query($conn , 'UNLOCK TABLES');) O# R# d9 ]2 N3 W+ R
- //查询解锁后的id值
" b! `+ y* l, @) i9 U1 { - // $res = mysqli_query($conn , 'SELECT id FROM ta');% d% S: N' ~4 ^$ X3 Q, p% |
- // while($row = mysqli_fetch_assoc($res))/ O, W. ]3 s! E: q4 T0 g
- // {- d7 w# u4 M' g' Z s2 d
- // $id = $row['id'];
% H! u. v- X! @2 E( I* m - // }
: ]5 M) o7 B$ V. \ w9 j. L- M - // echo $id;
复制代码
- A; U4 n! V- {9 S2 v4 a
9 H1 e7 J) Z5 U" C1 }
: X3 u3 F! h' g- s+ E' ^" X# kPHP文件锁示例: - /*
7 t# Q( f; B; C t" Y* r. s, s - 模拟秒杀活动-- 商品100件
- w8 { Q3 `' J& j+ x - CREATE TABLE ta
, C- m# I9 ?4 |( y# c - (
v6 j0 N+ h4 r - id int comment '模拟100件活动商品的数量'
; m8 D5 v0 Y1 p* \/ g - );
. j1 s T4 H! v" b - INSERT INTO ta VALUES(100);
3 b+ c i2 C( B' a" y! X5 ]5 u9 g - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
$ J/ Q% d2 X$ d) F- i6 w6 G8 K - */
T& k* e3 h* [4 b - 2 G2 V8 c" S q! }. ?
- // 关闭错误报告2 u& G ~( q* L! a* v
- error_reporting(0);
! u/ d9 O: _7 h% n5 X* e - 5 X+ \( W" z% z
- $dbhost = 'localhost:3306'; // mysql服务器主机地址: y4 ^2 o6 O) U5 A! ^" m
- $dbuser = 'root'; // mysql用户名
! k3 T5 `& U$ e' M6 z% X" {3 W - $dbpass = 'root'; // mysql用户名密码
! a5 w; Y! F8 i3 Z, k& N - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
* x+ K" ?' ?# E' L - if(! $conn )9 p/ g, C- {# e3 V4 b
- {
& e2 U) x5 g" n9 I4 A1 M- D7 e) t - die('连接失败: ' . mysqli_error($conn));: M. E: m' w+ g( ~5 F, n. y/ c
- }- ]6 B! g- J( G% A
- // 设置编码,防止中文乱码
4 c H; W, }4 Y* ` O - mysqli_query($conn , "set names utf8");6 b8 j" k% E/ R. O# V3 Y! E2 a
- mysqli_select_db( $conn, 'temp' );
& a G. J% q" _' C5 v
* Q S# P8 C8 w( j/ R R- # php中的文件锁 & a$ V# g0 t2 W( ]6 n) d
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 ! ~! ?6 i+ Z( Z
- flock($fp, LOCK_EX);// 排他锁 + `2 `9 e1 q/ v% d& y) C
- 8 Y: c# a; e; G+ W5 ]) W# n
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); ' W" n% w3 M7 K" @
- while($row = mysqli_fetch_assoc($retval))
7 o4 A5 u2 z$ R, o; a( o - {# u" ^# Y% k; E u$ C* B1 I
- $id = $row['id'];
6 e5 o% z& W. N8 V) O8 L - }
3 f; n2 ]! a8 Z- U0 c+ q -
" Q+ K! o* R; X5 w - if($id > 0)
; @) A5 j) G, G - { + ~& o% o3 _9 ^9 n, d
- --$id; $ b# _. x% f/ U9 h$ h
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
7 z/ b- B7 e Y. E, t9 F - }
5 ^4 H V" c( G0 W - # php的文件锁,释放锁
* M# t) Y% Q" k, ]/ H* e) o - flock($fp, LOCK_UN);
/ e% r, z( e* X f7 o# ^. z - fclose($fp);
! z& K7 L N2 o - ! x( f. K+ Q8 ]* D
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
- ?. X7 J. p" y6 @2 c! N( e$ a - // while($row = mysqli_fetch_assoc($res))2 a4 p: m5 U' z% y
- // {
/ O. \$ i% A3 d# |5 L/ D - // $id = $row['id'];
" S7 N; P8 `- C# ^. _+ ] f" T - // }
8 ~; x0 \5 U5 a8 ?# f5 J - // echo $id;
复制代码
' S6 U: Q9 A3 l* K" O, L- Q+ @5 {9 E0 I& e, j0 k% ]
抢券活动实例: - public function envelopeSnatching(){! ?& F. p0 U5 S
- $lingqu = $_POST['type'];
! I! u& ~% o5 E1 b' [7 \ - $uid=session('u_id');//用户id8 X2 i) d0 T* y j
- if(!$uid){
7 f. l; g" o9 B: ] - $data['msg']='您没登录,请先登录!';: E/ w: |* ?" j( c
- }else if(date('Y-m-d') != '2017-12-12'){' u9 L2 n8 E5 r% h
- $data['msg']='不在活动时间内!';
" U/ h% B9 B/ ^* Y5 @ - }else{
, p# N' t$ d7 z8 O" ?( F - $hours=date('H');//当前小时数, j. F1 l4 [ H( U* `3 ~( u/ R
- if($hours > '09' || $hours > '17'){
0 I. K( g% I# u! [; J7 l0 z8 S8 t - % c0 x- Z) F, K- v8 B# h4 F" }
- if($lingqu == 1 || $lingqu ==2){//点击10点的% d2 R- w- j: S5 @
- if($lingqu == 1){3 K6 s( H( W, }) c; X& L* W5 g' |) B
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
7 {6 n1 Q7 I2 f, E - if($hours > '09'){" y6 |4 V1 Z( O
- $num=mt_rand(25,28);//优惠券金额
% M* w# }! ]/ _ - $id=1;
- t$ U9 Y9 A0 j: k8 {# i - }6 d* a" U, K' ], P+ `6 _2 v* X" Z6 L
- }else if($lingqu == 2){9 E& k2 T- U: k: M6 S( d7 q! o
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();9 S- e1 q! ^" _, L
- if($hours > '17'){5 ?; R$ u% ~$ p* m* J
- $num=mt_rand(50,55);//优惠券金额+ o! ]) m1 o0 E& n; e$ Z
- $id=2;. k; J6 r4 ^* x
- }$ U" c" ]8 _5 n$ D- B! v: W
- }. y. h2 A' A* s
- if(!$id){4 g7 D* m- a1 s* Z2 y2 H' u
- $data['msg']='时间还没到,晚点再来吧。';0 [: ?5 C8 p' ]0 B. B! t w
- }else{$ F* m$ r$ b8 x/ s1 E
- if($is_lingqu){, C: E) P8 c4 ]& e% f' a
- $data['msg']='你已经领取过了,留点给别人吧!';4 \- @$ G* \3 z
- }else{8 s% L- S: t9 H' p, U( r6 `" g
- //锁表 W" a5 b0 l* N( X+ \+ ^* O
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');) g" \. y6 U* R1 H
- $active=M('active_num')->where(array('id'=>$id))->find();
* R; e) w6 g5 _ - if($active > 0){
7 S7 S5 H3 n: ~- o" w - //开启事务( O, J# Y4 D. E) V# z- s4 L* V
- M()->execute('start transaction');
$ ]) k5 I( X& {2 B+ P" O - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
2 ^- C5 r7 y- l - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));+ M, X$ Y9 C% ~
- $members_preferential = M('members_preferential');. [. O1 X3 {: C0 N6 {8 |5 _
- //对应投资金额,3 Z9 [. i4 b$ e) l8 M
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
9 m; O) y/ `9 n3 f4 i - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');) e8 p% H# H, Z, B9 v5 h6 f q
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
1 f4 P, _% _3 A! i1 j% A' S3 r - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券" y+ E+ R! ^2 V+ P! M( a; U
- if($save && $add && $add2){
: H" ^* m7 _& ` o - //事务提交
% g9 j3 t- |/ c7 n - M()->execute('commit');) ]" z' b: o; f5 b6 L r
- $data['msg']='恭喜你领取了'.$num.'元优惠券';7 R+ x$ F8 c& a0 y' \( F
- }else{+ R- _" V4 x5 R4 j9 T9 S
- //回滚
8 b; C( o+ v) s* N w - M()->execute('rollback');
" L/ ^) G3 j0 d% l% o$ k3 K& o - $data['msg']='未知错误!';
% ?6 t0 N, q4 @8 v, F$ C" r - }
; o& E! l: ^) p9 ?. @ - }else{
( K" x7 f- G* u" a E }' L1 h - $data['msg']='红包已领完,你来晚了!';+ F' M3 W9 f6 z, n) W& s4 a
- }
6 l# J- t- @6 o* p# _5 O - M()->execute('UNLOCK TABLES');
: I$ W1 V/ @+ e+ m7 C( g - }
+ j# q! p2 I$ d" q6 @ - }
+ e) m' A* v7 G: P4 i- h2 g3 ?& { - }else{0 {7 T; c8 V$ S: l0 C9 l5 A
- $data['msg']='非法操作!';( |: T% t7 t8 R
- }
+ e6 K. F8 k# T6 u8 O7 _ - }else{
) D7 {5 G! T8 S* k5 B/ ^( W - $data['msg']='还没有到活动时间,请晚点再来哟!!';
k b& ~$ D8 K8 w: `$ ?8 f- Y - }3 M3 r" g8 q9 L5 Y1 m+ X
- }
b# G$ P" P; f6 v5 F - exit(json_encode($data)); B, Y2 S" C' }# P: I" v/ v/ r
- }
复制代码 ! {0 {, t5 [$ a6 S, X* d
; K4 I5 s/ E) q+ v7 L+ [7 j
|