|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
' f+ T& ^$ U$ S. q3 uMysql中的锁语法:/ Q% g2 E0 O$ k9 r/ |$ [# B8 s- H
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
" q+ l" O! o* |1 E' NUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表) v# F# y# @" f+ q% V3 [8 @2 t
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞8 I- D7 ]; y. H* `2 o
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :+ M+ d7 F8 [2 Q) ]# Q
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。8 @# m0 ?* q$ n' Q |* T% Y
测试时,有个文件就行,叫什么名无所谓 总结:# Y; A. T! P. i+ J! N6 i* v
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:2 v5 @) g" L8 z9 ^' I0 R( i
1. 高并发下单时,减库存量时要加锁
, A3 Z/ [* n4 |1 z2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
3 R6 l7 r3 g$ v - 模拟秒杀活动-- 商品100件
+ e0 u- x' `. v+ p* ? n x - CREATE TABLE ta
6 K, t1 d2 m0 m+ w p1 E" O - (2 c8 [$ |8 B3 n
- id int comment '模拟100件活动商品的数量'! y/ f' ^1 V( z! s! e
- );9 H# }4 \, c2 w2 [
- INSERT INTO ta VALUES(100);
$ Y8 Y( L( C9 j4 M4 R - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
4 u6 ?# X- z- d6 y0 B5 c - */
5 J* c- ]2 ?' Z -
' {4 j& e2 l) N& o% ?4 M& {! N - // 关闭错误报告7 Q( l6 k# n' {0 B$ M
- error_reporting(0); T/ O& g* c2 Q
- . D* m. K3 f& z1 D. |4 U
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
* v! y* u/ F8 v1 Y& e - $dbuser = 'root'; // mysql用户名 @& O( _3 M$ ?+ U2 Z+ @' n
- $dbpass = 'root'; // mysql用户名密码
/ K: s* }/ v( q8 J) t - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);+ x/ k5 a3 l7 O7 i# a2 n1 x
- if(! $conn ), u4 h6 O! j% `' s
- {
& K6 k% L) a' I b4 Z) o - die('连接失败: ' . mysqli_error($conn));
. P' C9 i' f; C, a* E1 B4 p w7 O4 U8 t - }1 Z; z. N" W2 z8 Z( V0 Q5 i- e* z2 c
- // 设置编码,防止中文乱码
5 x* f! F) p, w - mysqli_query($conn , "set names utf8");) b }1 w7 @! v2 u- y4 u
- mysqli_select_db( $conn, 'temp' ); + M$ v. a2 G; }7 W
- 1 v; `9 }1 ]0 K2 s% b! `1 {
- # mysql 锁
x( y Y! M6 [6 z$ W - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
: F: W+ V5 Y9 v, ]5 R ^ - $rs = mysqli_query($conn , 'SELECT id FROM a'); 9 x! ^% N* q) p! K0 W
- $id = mysqli_result($rs, 0, 0);
1 i5 h% F2 y! K0 s" m: {9 K4 u - if($id > 0)
7 U% V( e6 {1 s0 n9 P( { - { ) d) d; [0 `+ b1 O. c8 J! x6 n/ b
- --$id;
& l% j- H' t! S, h - mysqli_query($conn , 'UPDATE a SET id='.$id);
- G. T9 B( V$ T; a% N/ m4 u - } $ c+ k3 Q$ y6 |, w( ]1 J' ~% I
% h& ^' g$ ]6 l4 o7 G- # mysql 解锁 1 Q1 o; i$ ?: a! k8 [0 Q/ e
- mysqli_query($conn , 'UNLOCK TABLES');* o9 }" v2 M1 E: A6 E
- //查询解锁后的id值2 o# Y+ {. u, I# W7 S+ o. v p
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
: Y3 g, A% d( a1 `; n' ?7 C1 ]: {' e8 Z - // while($row = mysqli_fetch_assoc($res))
' F3 g- S/ ]: P7 ^7 P0 O$ p0 Y' r - // {
, W* R# x y$ Q1 ~8 H1 N - // $id = $row['id'];
) E- k3 o4 U; @ - // }+ z: Z. T+ T* J! c v
- // echo $id;
复制代码
5 A5 H0 {* l: x8 E8 Y5 E
u7 ~3 ?. R+ ]
) L6 J! X: X9 S& z+ Z/ j/ F' q+ KPHP文件锁示例: - /*
9 e: S/ c7 y/ ]+ R% t" h! ? - 模拟秒杀活动-- 商品100件6 w7 b9 Z" G; }) \2 C Z
- CREATE TABLE ta- d) ]8 R8 ]9 e2 Q2 e5 F, f
- (6 T1 E; L4 B# z1 |# a) u, C
- id int comment '模拟100件活动商品的数量'7 k, w: z" F$ x# s z3 M0 w
- );
5 Y8 V% C8 B6 ~# W% S7 t# p& L: m - INSERT INTO ta VALUES(100);
9 a7 [1 V" q- G- |& c - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件3 B) p" X$ a6 X' d4 ^, x' [
- */
% N& P, o5 n2 ?6 ^- a' X -
1 u- c' A5 }5 g2 u - // 关闭错误报告* b7 `( e7 t: j
- error_reporting(0);
4 g% D- T6 B% R) ^" P
% _! A5 Q+ A! ?, D- $dbhost = 'localhost:3306'; // mysql服务器主机地址, e+ Q1 N, M7 E
- $dbuser = 'root'; // mysql用户名7 T. I7 |4 y/ ?# H
- $dbpass = 'root'; // mysql用户名密码
. N2 h" B. g: G/ F - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);$ q3 U8 }5 P/ L/ Z- W" j
- if(! $conn )1 v0 J. U; W9 }" K
- {" i4 x5 ?" R* L4 f# H# [
- die('连接失败: ' . mysqli_error($conn));: P' \3 S' F5 v" k
- }
4 a" j t( |5 s3 g9 U2 C- s - // 设置编码,防止中文乱码
2 k9 ^; P/ G/ u/ j' m1 c7 j# ~ a - mysqli_query($conn , "set names utf8");3 R' ?- [: t1 D9 }6 q
- mysqli_select_db( $conn, 'temp' ); ! x# M0 P3 j/ k) m4 t
- & g2 M2 E( V2 Y# [) { H6 Y
- # php中的文件锁 + m7 h0 K8 j; Q
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
: m0 j8 t! T. ]& F8 n1 t7 i - flock($fp, LOCK_EX);// 排他锁 2 N V$ |0 |+ z; {
- $ g& s2 O5 x/ Y/ O0 Q2 t+ q
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
, H" B1 s) J! i4 z+ o, u a k - while($row = mysqli_fetch_assoc($retval))' {( U5 ?% B, U7 \
- {
3 @+ z( T( u$ j4 I, r: ~) v8 D - $id = $row['id'];7 u7 a* I: _" h' p
- }
- T- I0 @ ^" O/ f - 6 i! S4 s9 z; D) F8 J
- if($id > 0) # I3 z- k- b3 O$ \ t
- { : }7 P4 j6 f7 G2 E* N
- --$id;
^ \0 C h: v6 w8 M. r" H' q& C - mysqli_query($conn ,'UPDATE ta SET id='.$id);
& C7 a: j6 [' H - } $ E ~5 i3 u1 z t6 |4 x& U& I# e* m
- # php的文件锁,释放锁 9 q; K- |4 U6 E) ^: F
- flock($fp, LOCK_UN);
+ R+ J; _/ [, T$ f* U, u - fclose($fp);
2 H7 i: @$ [1 t! q0 o* s5 `# Q - 5 c& D4 t' \; y' r% y- Z
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
% ?% Z% S5 O$ |$ \ - // while($row = mysqli_fetch_assoc($res))' v; m$ p. c; l9 [; `( z" N8 F
- // {- c$ b' e8 X" J- W( T
- // $id = $row['id'];
/ ]4 D% q4 g; n. a" J - // }( X3 [) G4 d, D* d0 W8 K
- // echo $id;
复制代码 * R( j, ?% ?* n' f3 `/ M
! a" ^/ {' u4 W5 r5 k) Z抢券活动实例: - public function envelopeSnatching(){; v- r1 ]0 k; b9 C: s3 L- ?
- $lingqu = $_POST['type'];! b/ y& x) U( @3 y
- $uid=session('u_id');//用户id' w6 f2 {1 o; _* w2 @
- if(!$uid){8 J4 F- y& o' @( _5 D3 D
- $data['msg']='您没登录,请先登录!';
6 o7 X+ r8 m! W3 r2 { - }else if(date('Y-m-d') != '2017-12-12'){8 ^$ T5 E% J/ c" \
- $data['msg']='不在活动时间内!';' s0 t+ i- k. y2 l
- }else{9 M! m: ?7 I; Z, l5 u0 Q
- $hours=date('H');//当前小时数0 b) D5 U |& _9 F7 r+ o
- if($hours > '09' || $hours > '17'){
, t2 [$ `! q" R( Q+ J, P6 p a
: ~0 D( |6 J' `5 J) |, D, I- if($lingqu == 1 || $lingqu ==2){//点击10点的
" g0 W$ M! [' E. K. D8 ]% P - if($lingqu == 1){) e! a9 ~1 q$ F: { f- ]& `
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
5 d# H+ c# c o& P2 f - if($hours > '09'){% D0 @1 `, f% k+ {( F6 a# y( \
- $num=mt_rand(25,28);//优惠券金额
) p) h9 P9 V5 A# a$ o" n - $id=1;1 F) v1 T1 v4 n# n
- }- @/ ~- n: ^ s# d0 s. Y
- }else if($lingqu == 2){
" @, k. L" G1 H! |- Y" | - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
2 ^ K1 L) x* { - if($hours > '17'){0 C6 X* ^& E5 A7 N
- $num=mt_rand(50,55);//优惠券金额' c# d2 ^7 J3 R. [: f; \
- $id=2;# ^; l9 I4 }7 H8 F
- }6 @8 q! _& H$ b4 O6 q5 s0 d5 A
- }- E T9 x$ W1 s8 d1 T. s" d
- if(!$id){; v3 N* j. k, M( ~
- $data['msg']='时间还没到,晚点再来吧。';
4 s' C7 Y) r: x7 W: T' l - }else{
- [' s( Q7 h2 e0 k; _1 Y3 c+ N - if($is_lingqu){
i; D' N9 i2 c) u5 N6 x - $data['msg']='你已经领取过了,留点给别人吧!';
5 A) F% D: P. r( r& V - }else{
! X* E$ j# C6 X0 P; q8 z$ p- K" v - //锁表
% u5 w8 @: y3 `/ j6 y$ E1 H - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');* m8 K+ ` r' X: {- Y; c
- $active=M('active_num')->where(array('id'=>$id))->find();
* F, N2 B8 s' e - if($active > 0){4 I0 `; l& n2 t1 I/ Y8 M
- //开启事务
5 \+ `- J3 ^' i7 T - M()->execute('start transaction');
9 s& |5 u8 ?1 F9 @1 Q+ } - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));: f+ _8 l5 _2 f0 [, d, \3 B
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));$ C8 |+ a9 K( t2 T
- $members_preferential = M('members_preferential');8 j% k* |+ |+ d w( N
- //对应投资金额,
) c7 N0 h$ m4 w8 R2 j - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
$ _6 d. ~9 C9 ~! G; S+ t - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');4 V8 k0 h+ z+ T* c7 L
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间! _0 k/ s) T5 Q
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
& q% W2 V6 |0 p T' @ v - if($save && $add && $add2){
* X7 T$ [; x: N9 }( v$ m - //事务提交1 H# M) d, h3 r+ {
- M()->execute('commit');
( B. P' @7 t- \ - $data['msg']='恭喜你领取了'.$num.'元优惠券';! T4 y% x0 h7 a. ~+ p/ Y/ t
- }else{
0 T2 B) r' f3 ^$ S9 F - //回滚" q3 d! Y: E, `# J$ G- U
- M()->execute('rollback');
) I& Y1 G1 X9 N - $data['msg']='未知错误!';
+ M5 A7 X7 k3 h. r' e. t# k0 ~ - }0 f3 z. T* O2 {1 O
- }else{
2 s6 j- Y+ e: p- c' \8 A - $data['msg']='红包已领完,你来晚了!';' K; u# L4 o) F7 p7 E
- }
# H; k# e+ p4 ]# V( d - M()->execute('UNLOCK TABLES');; c" P; m5 q8 L. F* N, {% Y7 K8 k, m. z" i
- }- ~8 Y3 L$ H7 D+ G! C
- }
8 O4 i+ j" L5 ~0 {4 l, s4 G - }else{( _! w0 a4 _( K
- $data['msg']='非法操作!';$ j# e1 ?0 {0 x' r4 C
- }
5 ?, U/ @0 V5 H: Q2 D' R4 y - }else{* x: }( e1 X0 O! c z! i) `
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
- p6 s# k( A2 |5 D6 F - }
6 ?0 D6 l2 K% i7 L8 w- J( [& a - }
F: f% [: w: d+ _# H - exit(json_encode($data));* w5 i& @3 |5 }4 m7 o5 I" m' B% J
- }
复制代码 6 {* c( d: o& E7 Z4 H
! j% _& O. K; {( g+ i5 d& ~ |