模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 - K( q# l) c4 t( [1 k
Mysql中的锁语法:" }' e6 l$ I7 N# g, f) ?
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
$ {8 K/ R1 f' p, u) _2 TUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
$ i6 d9 T. j( F; w+ i7 Z2 YWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
/ b5 l. X; f: V+ Q* [0 w- T# K注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
& J. l7 t3 M$ C) b& b9 o5 K文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
1 J0 T- `9 u& _# f! k测试时,有个文件就行,叫什么名无所谓 总结:( B g& `# j1 Y' x$ P8 x: U6 `+ b% F
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:, c, B0 b3 Q7 t3 [' P
1. 高并发下单时,减库存量时要加锁
! }. M& W; b; h. B3 p! [1 B2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*6 {/ r) n* R: n4 F
- 模拟秒杀活动-- 商品100件
) v5 Z ^. L/ B8 a/ O6 I - CREATE TABLE ta1 p% E7 c9 `" J0 v6 j$ M
- (( |4 u3 p. F( |. V9 Q3 v: Z
- id int comment '模拟100件活动商品的数量'% k8 z1 n1 J4 Z$ Y1 u2 r7 S
- );) n& G: y# `7 b! h
- INSERT INTO ta VALUES(100);
0 F% w, O! z! E1 Q# c' z9 w& } - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件% W# j3 C$ f% b& C7 ^ k8 Y- i
- */
* ~7 k4 I ^' A9 l5 h3 v. M( X# k - $ n! s- f9 J2 ^$ D: P+ Y
- // 关闭错误报告1 K$ `9 h: Z1 G+ f: s
- error_reporting(0); 0 `6 S) ~( @: ~
$ [/ M( Z6 m7 X* D- $dbhost = 'localhost:3306'; // mysql服务器主机地址, p' O/ ]7 K3 S- `
- $dbuser = 'root'; // mysql用户名
- R/ u' L* Y' {# K2 d - $dbpass = 'root'; // mysql用户名密码
9 e9 G' C9 a- D- S - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);- [9 T1 V. w' L- u. t- L
- if(! $conn ); j% i3 ]: g# i# H; { A
- {
8 C1 N7 O3 v9 y( u - die('连接失败: ' . mysqli_error($conn));
[0 N+ _7 A( u5 s - }/ t; U3 [% C. E/ {
- // 设置编码,防止中文乱码( ] ?1 ] L7 }5 V% E8 r
- mysqli_query($conn , "set names utf8");, p) r" N. d+ Y: W) t" B$ ^
- mysqli_select_db( $conn, 'temp' );
7 y |9 [4 q0 O, D4 u2 Z* @
0 @! m9 D: T. E: x3 p- z- # mysql 锁 1 O7 O5 {' V( k6 s+ W
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
( f5 L; C" ]5 i E$ F! t* P - $rs = mysqli_query($conn , 'SELECT id FROM a'); h z# e# p5 G
- $id = mysqli_result($rs, 0, 0);
7 O) z5 F0 r2 { O, F4 p - if($id > 0)
) W+ p) u) x' `7 W! l - {
3 b4 Z# k6 J3 Z" X! p1 t+ d2 r - --$id;
3 @7 p f# z/ P3 n - mysqli_query($conn , 'UPDATE a SET id='.$id); 9 N6 Y' {7 \. `% B0 U
- }
; w* a7 n1 o0 K - $ d: Z8 O, ^0 x
- # mysql 解锁 , U' `; s) E2 ~* k- ]# @& X
- mysqli_query($conn , 'UNLOCK TABLES');! a. Y% r% W4 e/ k4 h
- //查询解锁后的id值0 e( y) l# o3 V: k1 m
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
$ v& P# J4 q. a F% _4 y - // while($row = mysqli_fetch_assoc($res))) F0 @5 F) |1 T2 A* G
- // {
: @# n3 B1 R9 T, ?$ a: X - // $id = $row['id'];. D$ {! ]$ l+ ` c# U
- // }8 \/ B0 ~8 b. W4 u) ^5 a
- // echo $id;
复制代码 - y1 a3 d0 \+ I2 P2 e; M
! m7 r# S- [% l4 R0 Y, @4 E7 k$ p5 Y3 r) I/ D) E
PHP文件锁示例: - /*1 J* Z" Z' k8 \. X
- 模拟秒杀活动-- 商品100件* l9 m/ y7 I. [
- CREATE TABLE ta
* p0 d8 d0 G, L0 M1 Y( b( j( c - (6 x% E) j& N7 ~/ d3 u) q5 i
- id int comment '模拟100件活动商品的数量'+ M# r% B+ l7 l4 V$ v6 _$ k' J1 B
- );
* r0 j! z" S9 N+ ?# h# m - INSERT INTO ta VALUES(100);
# \1 c5 k7 O0 R7 a - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件! K1 f# E: ? ~* ?& I! K! Y
- */
; ~; ]) R- M J6 p0 ]. x7 k - . W/ b! v. ^* a% T( q z
- // 关闭错误报告
' A8 R) k1 P- @# j8 k3 Q/ T' [ - error_reporting(0);
, d0 w! u3 N& `/ Q: v( x* |
' d, C& B+ q5 w! z5 o- $dbhost = 'localhost:3306'; // mysql服务器主机地址; g) [4 k& P! k: L
- $dbuser = 'root'; // mysql用户名0 e0 z7 ]' W. j& j# J
- $dbpass = 'root'; // mysql用户名密码6 l5 M6 p+ S0 A4 E/ b n
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);0 n6 ^8 U6 h% \6 Y1 u$ O
- if(! $conn )
1 l, g F4 M1 j& G+ Y1 e- `% P - {; u Q$ Z. ?5 \. Q' M/ f
- die('连接失败: ' . mysqli_error($conn));
4 A' e$ \: }% K - }
: h: I( U( r; v - // 设置编码,防止中文乱码
: q+ I. ?! a/ t8 g - mysqli_query($conn , "set names utf8");
9 R$ z m& F# N z# _ - mysqli_select_db( $conn, 'temp' ); 5 d( M8 f# {( Z4 z+ X. |
! n1 C4 l6 O" @3 u- # php中的文件锁
, M6 h3 M" z$ x* e" Y; ` - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 " Z% o/ u+ K. T* ~6 W4 H' z
- flock($fp, LOCK_EX);// 排他锁 * G* t: E9 }6 c8 N
- 6 z6 L" y6 L0 q6 [% t* E
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 3 G# i3 p* y2 g6 f3 {" ~
- while($row = mysqli_fetch_assoc($retval))7 K8 Z2 R8 r( Y( n
- {6 Y+ M0 L" \% _3 l! y
- $id = $row['id'];
4 S1 V b( V) x: ^ - }
, _' x: K& ~: u% R5 ? - + e" |0 d; w+ b/ a( R/ [1 b$ ]& m) p
- if($id > 0)
* f; I0 i, |( R6 t$ _ - {
* z3 s. C6 ^% I' v ^ - --$id; 2 h! a2 o; q& n0 g& O9 e
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
" m, T2 j& ?( P; |5 E9 t - }
& u5 T# s- }8 g; @# b7 B7 F4 I0 ? - # php的文件锁,释放锁
) l8 h/ E9 C% Y/ y - flock($fp, LOCK_UN); : G8 m" v4 ]7 {. m6 _7 U
- fclose($fp);, S7 f c9 I) s8 C7 _- `4 y& G- i) G" {; ]
- 2 F. {. b# z% j( K
- // $res = mysqli_query($conn , 'SELECT id FROM ta');7 \7 j0 r$ K4 u
- // while($row = mysqli_fetch_assoc($res))& J9 k) |0 P& M4 `1 S
- // {$ o ? e% ^; J2 z
- // $id = $row['id'];, K8 u' D+ v3 _2 W3 [' X7 P
- // }3 B Z8 V+ r* g
- // echo $id;
复制代码 8 n8 r4 B) l) W9 A
$ ]6 f3 c: I& P* U( J抢券活动实例: - public function envelopeSnatching(){
& H2 a4 y4 i1 A5 X7 y: f( [" T% v - $lingqu = $_POST['type'];! r D2 n* Z; w9 U
- $uid=session('u_id');//用户id# m# G' p- r0 Q1 i3 D1 a
- if(!$uid){+ g, j4 w4 p" P, _5 E
- $data['msg']='您没登录,请先登录!';$ v( t8 Q# e7 G! A6 P0 h
- }else if(date('Y-m-d') != '2017-12-12'){: D$ @3 K( |8 Q
- $data['msg']='不在活动时间内!';
( y3 p( I* ?- F& h6 x1 H - }else{. `$ B% p) H6 k( L$ M' |; L
- $hours=date('H');//当前小时数
/ p# g( \6 b7 V' Y8 S5 j* O - if($hours > '09' || $hours > '17'){
2 |) u% f& @% O* Z+ a: n8 M m% b$ { - b* z, b4 \ v" L
- if($lingqu == 1 || $lingqu ==2){//点击10点的. j1 Y) Y+ x _* o6 c$ q
- if($lingqu == 1){7 l2 \' ^5 `3 V" P
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过# l" q D; N/ v. ^4 d
- if($hours > '09'){
' e+ i3 ~; A2 ?- i+ t5 L2 E5 i) R - $num=mt_rand(25,28);//优惠券金额. v( I0 J$ l8 z: P" q6 |* @
- $id=1;
' N% y) m- t! {, R4 C - }
- q! e6 x$ V+ ]+ W( z7 T, M' u8 ^ - }else if($lingqu == 2){
. U" ]8 c7 m( r( R - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();% A' W/ C. ^4 a6 [, D O
- if($hours > '17'){
! W$ x' F! ]% p- ]" N: `. } - $num=mt_rand(50,55);//优惠券金额
* b: l$ v) ~/ ^& A+ k) N - $id=2;' j. B2 h- e' U. H" Z0 i
- }; q" ~0 a9 C) [9 l A
- }
8 c* v q% x; J7 [& y - if(!$id){) u+ z2 f. V# G" Z' V4 T5 P
- $data['msg']='时间还没到,晚点再来吧。';
) t/ I% c; w% p' | - }else{* a5 J7 n: z* D. `9 }# @
- if($is_lingqu){+ B6 q- \8 V6 `/ q) q9 d3 |
- $data['msg']='你已经领取过了,留点给别人吧!';
% Y6 c1 R) j8 e0 A - }else{
$ [: L; p- ?, n3 L - //锁表" N9 r! Z: k1 s
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
" O% u( w/ m8 @+ o - $active=M('active_num')->where(array('id'=>$id))->find();9 _$ r# Y0 F+ D V2 |, y2 Q
- if($active > 0){
: l* k/ c B0 i# j5 E6 l - //开启事务- |( _4 {3 h% G. i: i
- M()->execute('start transaction');% Z/ F; i: i% G8 t' L
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));# d! a% `6 G+ }! I& a& T7 R S
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));6 p# Q# ^* b) R) I0 l+ F+ L
- $members_preferential = M('members_preferential');. ?( @: T2 k/ k* M9 H$ {
- //对应投资金额,
; [5 g8 B( k; {% X/ h! w - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
) H$ u6 v) \1 p O' g: r9 p - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');! a8 I# y4 G; V8 c L4 o. ]
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间# m' A) b6 e" H9 O1 q
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券6 A8 H5 L; W) ]$ M/ X
- if($save && $add && $add2){
4 k y4 |) l8 q& L' @/ j - //事务提交# K& y: q5 [0 q2 C6 R) m: K) N
- M()->execute('commit');
: }* Z, d" J1 T+ |; ^ - $data['msg']='恭喜你领取了'.$num.'元优惠券';
( R) C0 x0 i- ~' L8 g; V. [6 V( r - }else{
! p+ K# a# f: n1 R8 `, l: l7 F - //回滚# a: i7 B( V/ w' |
- M()->execute('rollback');
) u P' W) D- H. p+ t9 n) J - $data['msg']='未知错误!';: j1 }* {* ^% i$ g" V0 I
- }
; t, c1 [' p. X$ ]6 u# W - }else{
4 J$ E$ t1 G5 I# x1 N' m/ y - $data['msg']='红包已领完,你来晚了!';
# N/ B" r4 y7 D8 R2 T3 P - }. L |# }4 w9 P+ [2 e
- M()->execute('UNLOCK TABLES');$ m2 l" e% Z$ ]
- }2 E0 s# O0 E. a! h) I, P
- }
0 F& y, D+ u- d/ V; X- O! H" e - }else{' f3 G. @' F0 v: u& f5 b) G2 G
- $data['msg']='非法操作!';4 {& q9 E& V& i2 ^
- }; f! c+ H" [: Y1 P9 v$ {
- }else{2 R, s5 y$ M: K/ V* K& Q$ E( v
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
( R( X1 k; _ Y+ _6 G* P - }
% q8 A, u+ j/ s7 _% ~ - }
. ?0 A' Y) e, Y4 Y - exit(json_encode($data));
/ K v6 f8 F1 p6 i1 B - }
复制代码 ( D4 T+ e+ z" T
' V! ~. Q4 z5 G0 q
|