|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 9 M$ j7 H# K d1 Z
Mysql中的锁语法:" G# n; k0 F) g/ [! {
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
: M7 g7 t& v1 l; p1 A3 c$ B6 NUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表# f: Y% m* X1 Y+ a3 p
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
1 }* B+ ~, h! T% W/ k注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
4 [. E" g1 |5 \1 N* ~文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。, R/ q( E9 O2 V! @$ d: F
测试时,有个文件就行,叫什么名无所谓 总结:
) m q% z+ z. `项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:- e0 N( G# d. @4 M v( i7 ^/ ]
1. 高并发下单时,减库存量时要加锁
! J! A$ c4 I* U3 M2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*- }/ t" w' s+ k' D, v" U( O2 }
- 模拟秒杀活动-- 商品100件9 E' D' o7 N6 j' Q# L; Q
- CREATE TABLE ta
- ] U& @9 r5 f2 ^6 ~* X o - (* s* P+ a% b& p9 ?5 _$ X
- id int comment '模拟100件活动商品的数量': i8 m$ s( [3 y/ N7 L1 P9 M& X2 a- B
- );
$ O5 }. c$ r1 k# i8 ^$ K - INSERT INTO ta VALUES(100);
4 L8 i' s& A3 h - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件5 U# p( O# J8 C( R: r6 ~
- */
. t! S- W8 z( S5 A' w) H4 C6 ` -
* N" T3 b0 d6 ] V" R9 Q - // 关闭错误报告
: N$ w# f: S g/ R6 C) x - error_reporting(0);
: A- x6 L$ B* M - ) P( a% q% t6 {" t! s
- $dbhost = 'localhost:3306'; // mysql服务器主机地址0 k7 j5 z! F) _- O. D- X& o
- $dbuser = 'root'; // mysql用户名0 j" _ v9 Y' o: c1 ^( \3 @
- $dbpass = 'root'; // mysql用户名密码
6 f2 {! e8 n4 O' b' a0 L - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);4 ~$ E& F+ i* p+ Y5 c
- if(! $conn )
" V. d7 H! y: H, ^% L - {
4 ?% d4 ^+ b" x$ ^" I. I4 P! R" F - die('连接失败: ' . mysqli_error($conn));& O7 u3 g/ u: m5 @' G4 E- |/ @6 m
- }$ `9 [) @* U, E8 f
- // 设置编码,防止中文乱码/ s" {5 P9 B" |# s+ w
- mysqli_query($conn , "set names utf8");5 v+ F' p; V( W2 F
- mysqli_select_db( $conn, 'temp' ); & `" J# q6 ?) L/ ~3 \9 y9 D/ {0 X
* ^4 @8 U: ]7 f$ _- Y! ^. F- # mysql 锁
# o/ R6 k; |8 b" [0 g - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 y; v" d1 b/ ~1 X0 V8 [
- $rs = mysqli_query($conn , 'SELECT id FROM a');
( ^3 \, ], R& d( B- `6 `6 ~; `; ^4 M1 W - $id = mysqli_result($rs, 0, 0);
# D1 d1 W9 B2 ~ - if($id > 0)
: q" `9 Q3 o* ]) K - {
! O- J" K. }/ ~' c3 G$ V' T) A+ \ - --$id;
5 Z5 w) h/ e+ s) J - mysqli_query($conn , 'UPDATE a SET id='.$id);
$ |0 _$ h1 s7 F9 {; d - }
3 P% S7 x0 p4 w" _
7 f/ x! a* }+ v( P) D! |- # mysql 解锁 + t; H; G, }8 J, n6 l0 ]7 M
- mysqli_query($conn , 'UNLOCK TABLES');
# d) a0 {( h, X% \ - //查询解锁后的id值
; e) G, X, W! v6 l# Y - // $res = mysqli_query($conn , 'SELECT id FROM ta');
7 t: N v; v' t! ]- e6 z% n5 D - // while($row = mysqli_fetch_assoc($res))
7 b: x: \9 X0 h, T - // {, J* Z T" K4 P' t
- // $id = $row['id'];2 C( R, \* i4 ?0 @6 R4 r: h1 Y
- // }+ g: C( l; S( l# `; {1 l3 G
- // echo $id;
复制代码 * r8 ?) [& U4 \2 c
% k( L+ {& ?# g$ m. q+ {* n. E( v- n4 K6 p/ N, ^( d5 _% n: E
PHP文件锁示例: - /*
7 p) s4 h, u; e7 F - 模拟秒杀活动-- 商品100件
1 H" V6 ?1 k6 ?# ]1 E! x6 ` - CREATE TABLE ta
- `& ~$ m: u, @' n7 l$ \" s$ h - (
7 w: U, r9 K2 |- [' [; R - id int comment '模拟100件活动商品的数量'0 j1 n. c2 w7 K
- );
" P& J0 p3 W# C+ C' S, l, h - INSERT INTO ta VALUES(100);
/ o( G& r' p6 Z. W7 U - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
m/ C9 f$ T3 O3 Q4 R3 S - */
6 {; u& l: c- t- M! K1 E% V- d -
4 h; K1 O. R7 _9 Z' [* [ - // 关闭错误报告
& {% V9 n7 A- U+ `, N* R, |& B - error_reporting(0); 9 _4 X; x$ U8 |- \( _
2 w( }" I# j. Q- $dbhost = 'localhost:3306'; // mysql服务器主机地址
) O7 @ s% \% x' B2 s2 R! a9 W' P - $dbuser = 'root'; // mysql用户名7 {( L, [. r0 c f8 F% Z' J# n
- $dbpass = 'root'; // mysql用户名密码' Q; N: G* q$ [4 Q% U
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);: D0 r9 |! a( Z! k/ S
- if(! $conn )7 |4 m9 W- x& m$ |, _; I
- {% d. W3 L8 J" m5 J8 V
- die('连接失败: ' . mysqli_error($conn));
6 k, j$ d# @3 k - }$ h1 K, n/ L: h0 L0 e
- // 设置编码,防止中文乱码* O- n/ a$ A( V; y* o
- mysqli_query($conn , "set names utf8");
/ C% D' `/ g" g* {* V- s9 N - mysqli_select_db( $conn, 'temp' ); ' A( M$ ?) f; _" c
0 X" i, Z8 {8 R1 u- @/ S% Z- # php中的文件锁 $ `! Z$ H+ o9 Y" [. d7 p
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
5 x9 z4 V( z5 Y6 `9 A - flock($fp, LOCK_EX);// 排他锁 ( j# n2 y$ U4 ^6 a4 ]& W# r: k: g, M
5 ]$ S* c( W% {; V. z9 [# P- $retval = mysqli_query($conn ,'SELECT id FROM ta');
3 ^% `: Q& j- q3 q$ P7 t( b) z - while($row = mysqli_fetch_assoc($retval))/ }6 K& p) {4 }9 o+ g
- {/ B9 X% f$ M4 \4 H5 A
- $id = $row['id'];
4 r2 i+ `, w" y8 Q i. e: j( y - }+ n- q, {6 d' b! H2 }, w3 u2 O; T! O
- * u9 R! O I3 {: U7 B2 L+ ^" `
- if($id > 0) 4 u( |4 w h6 k# w% e; l7 F* a
- {
' }/ ^; w1 ?3 W# S, _, F) i" z E - --$id; * c/ J& X# `" Q" r
- mysqli_query($conn ,'UPDATE ta SET id='.$id); , N3 u* ~9 a$ p( K& o2 g
- }
j9 i. [( ]+ b, {4 R* f - # php的文件锁,释放锁
[% z9 M& j; L+ G) e - flock($fp, LOCK_UN); 4 H. {# q6 T2 h" F( X# M, Y
- fclose($fp);- N8 V4 J- E! v, M
- 4 f5 l _" @ `* ]6 }
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
; K% _. H9 @# s' h9 M) M* @, p! j - // while($row = mysqli_fetch_assoc($res))
{* R% I" F5 k2 L - // {
+ Z, ^# j; e9 ~7 w: L - // $id = $row['id'];" }; R0 F9 q* q; H3 z! N6 B( X
- // }
4 i' |' `* }3 ]; n - // echo $id;
复制代码 # W) E. @! d. w3 H- h4 M+ |
) I( g" V: j4 ^ Y
抢券活动实例: - public function envelopeSnatching(){
; A+ W4 v; y. x ?$ h$ L - $lingqu = $_POST['type'];$ \- E* M8 `4 z
- $uid=session('u_id');//用户id# T, b! y, a) U Q) b1 o) {
- if(!$uid){ P; P- }5 W, S( w
- $data['msg']='您没登录,请先登录!';
) j8 K2 r/ o$ c - }else if(date('Y-m-d') != '2017-12-12'){& ~3 b& U1 S: M W8 o. ~6 Y
- $data['msg']='不在活动时间内!';+ [ D# H$ |9 w6 n4 a& ^9 l
- }else{! F# Y5 W, S4 n, ?" P
- $hours=date('H');//当前小时数, W3 e% k* y! R; D
- if($hours > '09' || $hours > '17'){( I0 w+ P E: i) n
* E" U( g9 t5 E6 p" Z- H9 w# {- if($lingqu == 1 || $lingqu ==2){//点击10点的
6 j C9 s% O' e9 K9 ` - if($lingqu == 1){9 x' O2 I! K2 x" R! C
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
. I- q8 l, F5 T1 c$ C/ {% w - if($hours > '09'){
3 A& Z0 w% o" W7 ]+ ` - $num=mt_rand(25,28);//优惠券金额
8 ] O2 S' y% |: ^. e0 s4 X9 b( h - $id=1;
2 l# s9 v: G( W0 m' Z3 @# I - }7 ^8 F0 G0 @1 Z$ t
- }else if($lingqu == 2){
4 c% t8 O. f! U! b$ D - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();( S1 q4 ]( j+ Q; g8 u3 a8 j
- if($hours > '17'){$ A% X+ ?- g0 @- F8 n' y5 O1 k
- $num=mt_rand(50,55);//优惠券金额6 F$ m- `; ^! O/ }2 q
- $id=2;& U3 P2 w) ]0 w9 @1 w% ~
- }" X9 G6 N5 O2 v0 k3 z6 V
- }. m/ _1 k: G# h" B* G
- if(!$id){
7 n3 T2 ?" p; [* N - $data['msg']='时间还没到,晚点再来吧。';& u' m+ S: v* i6 A- N
- }else{
/ N* g- y$ ^5 S) ] - if($is_lingqu){& ^" L' b' P' p( a- i% N3 U
- $data['msg']='你已经领取过了,留点给别人吧!';& j6 z z8 v# b; f1 m
- }else{! e2 E j4 z3 T( [0 z9 A
- //锁表( {9 ]* [5 _7 V, B. U$ H9 s! Y1 d
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');) l4 u4 j" F. _. f' T+ u+ h" k2 C
- $active=M('active_num')->where(array('id'=>$id))->find(); ^% Y$ z# I2 c q8 f
- if($active > 0){
* b4 \: T* Z/ I% L: S$ P - //开启事务
) O* g1 K8 b( Y% h5 t0 v - M()->execute('start transaction');
8 W+ d1 _' I7 Z5 M, t0 w - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
& ?0 k9 }9 b A9 r1 b' v; a - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
T5 p1 Z+ J5 k4 } - $members_preferential = M('members_preferential');9 Q' v' X$ E# N0 U
- //对应投资金额,
1 X0 x0 R* e) N8 X1 s* k - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));) x! k* }1 }& M7 U. d+ M
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');; S" w, r2 Y. O; T5 |. A R
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
9 i2 I( l. S3 R2 M: K5 J - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券$ P' z. b% ?" r4 H; {/ j
- if($save && $add && $add2){
7 D+ o, ^6 A; g+ i, W! E - //事务提交
2 g. o y, q$ a' G0 D5 d5 t - M()->execute('commit');
" f1 H) \" [3 h l( I# g - $data['msg']='恭喜你领取了'.$num.'元优惠券'; b( I4 K8 A9 x; H: ?& Q& w
- }else{ K0 C# q) `- l% V! j
- //回滚2 P9 W. ] o5 i1 g% R9 n8 n9 z
- M()->execute('rollback');
( l: R: {+ b7 H, }, o; S- j - $data['msg']='未知错误!';2 j, g* o& d9 \
- }
6 u3 \2 n- I; x1 \- r8 E9 q2 A - }else{2 u2 I! G" y0 a+ O& j- J* u: S# P
- $data['msg']='红包已领完,你来晚了!';- ^% v8 n8 U6 f9 Z: R; }
- }; p" _* P6 ^' k) q/ R1 ^$ P3 z
- M()->execute('UNLOCK TABLES');
4 R* c( C% _( m. g6 {1 @/ v - }
- K( f1 E7 ?, L - }
: N9 k, W; j, N' K( y2 j# ]& p: P - }else{
, M) Z; p6 ~7 f - $data['msg']='非法操作!';1 h1 l* D* A5 e' c! U# `
- }! Q& H, Q# E7 Y: R6 f+ v! X
- }else{
$ c2 v0 l0 L9 e% u( x* o - $data['msg']='还没有到活动时间,请晚点再来哟!!';
- }+ N% D+ _1 U- | - }
, v4 X( a% I [3 n. u! B - }
, H; {* l! c3 J/ I - exit(json_encode($data));
) F J5 h3 r: i! j1 [0 _$ n& y' m6 ]% f - }
复制代码
9 R! I1 A+ a5 Q$ |$ y
5 P, X( M! }+ N8 F |