模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
7 [% R3 S" y) x! SMysql中的锁语法:/ Y6 e* v7 j4 ]& s* Q
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】. k2 O& ~" B- W) c6 F0 k% T6 F1 c
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
2 x" O% C: P: e9 pWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
8 M! f3 M k: g5 c$ i9 K r注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
) ]" V& q# J& r2 u) g文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。. M$ R6 J! j/ y
测试时,有个文件就行,叫什么名无所谓 总结:
: @. T" |: J, r9 z项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
$ g w0 O" ~- P* N+ C1. 高并发下单时,减库存量时要加锁9 l+ C3 q9 I8 h& n
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
8 k. D# C) v# q - 模拟秒杀活动-- 商品100件
2 c1 I x6 C* T2 Y8 O& D - CREATE TABLE ta/ g" @7 U3 c2 u0 s* q9 E5 g! d" F
- (
; G6 W) X' P# |. z# Z - id int comment '模拟100件活动商品的数量'
# D( R( g$ Y2 p: X5 c; v. r - );$ j# I0 @2 G1 H" e
- INSERT INTO ta VALUES(100);3 P' m7 R8 h7 M% i7 K8 M% J
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
5 I. o8 a( ]+ l2 u - */
) [; P- x. m3 X( S3 Y7 H - 5 Y4 A+ h8 a, e+ o$ s
- // 关闭错误报告
$ Y8 H& M' K4 B) R - error_reporting(0); 2 ~/ w8 E" a# l3 i( B! R( P4 K
- ( n w, ]3 `, |7 P4 g
- $dbhost = 'localhost:3306'; // mysql服务器主机地址% D; [! U' j7 H7 ~- Q
- $dbuser = 'root'; // mysql用户名9 ^: L, c5 S- j8 h ^* J9 V5 C7 M
- $dbpass = 'root'; // mysql用户名密码" r D! [8 v* d3 A
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);0 m \" l% f8 O! K2 I, V
- if(! $conn )
2 g- F$ O1 {- w; b! l - {% b, {+ b' d+ U; O: ]; @
- die('连接失败: ' . mysqli_error($conn));. b" l0 O1 i$ V
- }& ^: [! Q# g8 V2 H
- // 设置编码,防止中文乱码
9 D# _; V1 ^2 ]: K - mysqli_query($conn , "set names utf8");. I/ X$ l/ I* r* w6 K# }
- mysqli_select_db( $conn, 'temp' );
. H8 q3 u- _/ O& j3 J" V2 p
* [# P( h4 J* Q' F' j* K5 i- # mysql 锁
/ A4 T, E- {& `& V5 Y3 X' G - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 7 \) n- w8 p$ i- K) ^
- $rs = mysqli_query($conn , 'SELECT id FROM a'); - d) L+ o% [5 u _1 X! D! ~: h
- $id = mysqli_result($rs, 0, 0);
; q, ?% j q! n+ U6 y. b% P - if($id > 0)
' S/ ?( ?- v2 E" J% ~% N1 b - { 0 C) _+ L6 `2 I% h4 h
- --$id;
+ k+ B* Q" T" i - mysqli_query($conn , 'UPDATE a SET id='.$id); z3 q3 |& n* Z% X& `
- }
; a/ U1 x$ A6 G3 S' [ - . f! y& u) J/ u" s4 G
- # mysql 解锁 5 r2 L" F7 D- P4 d1 d
- mysqli_query($conn , 'UNLOCK TABLES');
+ n# s0 r) ]: D2 \' a9 y1 Y( Q2 g - //查询解锁后的id值4 {: y9 k7 B2 { x
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
, x, [. Z# D* v% g& z* y: T) l! w) l - // while($row = mysqli_fetch_assoc($res)), B* j. `; Z. E+ i, ]* V, a
- // {
5 h K$ s" b( z3 z& e" t1 F- R1 S - // $id = $row['id'];: F) ]8 P# y( N8 ]7 Z) S* v- F3 e- Z
- // }
2 d9 u, v; g6 Y' _ - // echo $id;
复制代码
5 e4 u7 Y% A @( ?+ @& Z+ g/ d7 }% u/ Y& E3 p9 u
, u& ^$ s3 [% `
PHP文件锁示例: - /*
% ^ Z9 R4 W0 D/ M3 } - 模拟秒杀活动-- 商品100件
+ Z2 M4 q3 y9 r# t! W7 W* k - CREATE TABLE ta
; _ _& f) T q5 Y4 P; Y# O6 B - (9 T8 D: n2 ]1 e
- id int comment '模拟100件活动商品的数量'
$ E/ `: z& {. H - );/ M. L( k! q. T |, m; f5 O
- INSERT INTO ta VALUES(100);
6 y. I2 U8 t8 ~/ z& u% Z5 U - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
- e3 ^- x5 o5 }6 a - */
% `, D0 b- H; Z. Y3 @9 @0 h+ N) Q - ' A3 y0 S, Q3 `
- // 关闭错误报告
& l% a: J+ ^) l& E' v3 b - error_reporting(0);
0 h: K) Q6 U1 V# F" e: c- I
T; R9 o+ J1 u5 ]5 a2 {3 d9 T- $dbhost = 'localhost:3306'; // mysql服务器主机地址( e( ?5 n7 c$ J' M9 ?( `! h5 V4 n
- $dbuser = 'root'; // mysql用户名! p& d1 f( D& a" @2 \3 z2 a
- $dbpass = 'root'; // mysql用户名密码3 V. s( W" T) M T5 ]
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
8 z7 S- ]0 @6 z' K0 d6 C - if(! $conn )
: z6 v* i: `1 m' Q - {- m" E# r9 u9 n, I4 f1 O+ |
- die('连接失败: ' . mysqli_error($conn));" S O c: w% w4 A
- }
4 o) [% k: E- P' d$ N9 A8 B - // 设置编码,防止中文乱码
' x. e4 z4 m% B2 @2 w - mysqli_query($conn , "set names utf8");+ J1 D8 ~$ t: A4 S$ J3 p
- mysqli_select_db( $conn, 'temp' );
* Y. `( ~9 m+ w6 b: @9 y5 ^
. T0 j& i7 i: Q( v4 t8 Z- # php中的文件锁
/ h+ B% n8 F4 W/ m+ ^7 i% \ - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 , j$ X( W3 J9 A
- flock($fp, LOCK_EX);// 排他锁 5 S7 U0 S( ~5 f. G Y. o3 S2 b
8 `; t m; c; B7 N% v0 Q- $retval = mysqli_query($conn ,'SELECT id FROM ta');
0 P/ h, U6 ~6 k- {. l - while($row = mysqli_fetch_assoc($retval))) ~$ `; B- W9 A# r( P& v/ Y5 n" Q$ U
- {: R1 {, L* S: ^' e; f+ K/ [
- $id = $row['id'];# g; Q: Y4 v- d5 L9 S
- }5 \# z$ V/ ]% ?6 a/ N" O
- + c S C3 }/ R. c! f( X
- if($id > 0)
V: }- b8 w; l9 x( I& W - {
K$ u2 x% G. m( { - --$id;
$ U# p' {- T& l9 k1 a - mysqli_query($conn ,'UPDATE ta SET id='.$id); ' b" P5 ~( a* i, c' D# K$ z
- } - B4 G! ^; T0 q$ m2 J9 w' n
- # php的文件锁,释放锁 ' S7 ^7 S6 E. k; E6 P6 S0 m
- flock($fp, LOCK_UN); & Z/ G- R8 v. A! |) b; t
- fclose($fp);
( s6 J* n# a# @5 A, K' m* x) s
& I* @% b$ G' c/ {2 R X5 {- // $res = mysqli_query($conn , 'SELECT id FROM ta');# a* N% [' V! i7 g; M
- // while($row = mysqli_fetch_assoc($res))5 o1 K7 s2 o( h2 o% N
- // {
6 Z' r. S/ m1 z- o; v; w* q% Q - // $id = $row['id'];0 B$ _1 ]3 Y/ C
- // }( ~ V; S) M4 D1 D) p
- // echo $id;
复制代码 3 d, J3 u3 ^% D
, X0 S# l" u4 o5 n" _- n* J# k' Y抢券活动实例: - public function envelopeSnatching(){
s' i# ]; @ V - $lingqu = $_POST['type'];0 p4 `. e! y* r' t% S$ v
- $uid=session('u_id');//用户id
& c8 {$ q; x4 G8 n% _% t - if(!$uid){7 g! ?1 R% Z* ^( v8 a
- $data['msg']='您没登录,请先登录!';
) [$ ?7 v" E. {( A) A - }else if(date('Y-m-d') != '2017-12-12'){( w J: W) N" |$ h/ ?5 v
- $data['msg']='不在活动时间内!';
0 S, `% y4 f! P; y6 o& o - }else{
$ h/ a9 \3 R3 W( ]) b z3 M) h - $hours=date('H');//当前小时数1 k3 E% G0 L4 {" F: m: Z3 V
- if($hours > '09' || $hours > '17'){
+ D+ j- y& W$ h5 c* |. {
7 g; D+ @2 W5 o4 J& ]- if($lingqu == 1 || $lingqu ==2){//点击10点的
0 ~- H' o. U& ~ K# T. b - if($lingqu == 1){ ]# y0 h: V# [) |' L1 J# `3 d3 ~0 M
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过& E8 [( M; i. U# }) `
- if($hours > '09'){6 E. r, }9 i( ?
- $num=mt_rand(25,28);//优惠券金额- F0 c. Q" _+ C/ j8 h5 N
- $id=1;! `' |. _& e+ c
- }
0 E. Q0 K5 n) d, s2 t - }else if($lingqu == 2){
: S- @5 W% U% d4 q! j - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();; C( z: j- ^# l1 V/ o
- if($hours > '17'){
y, ^* u4 A# s - $num=mt_rand(50,55);//优惠券金额0 d0 F$ T3 f8 g" x/ \ U: K
- $id=2;% C5 `% P3 M" a* ?, u. r
- }% F1 }2 d0 d) c$ M7 ?7 s' m
- }
) F4 q8 h+ ^. ?: ~2 O - if(!$id){$ S2 W5 {( k \' a
- $data['msg']='时间还没到,晚点再来吧。';
; }0 {+ _6 k$ z2 E4 T. ^2 f* y2 r - }else{
* t; Y) n, O9 Q$ _9 y q* \/ s3 W - if($is_lingqu){
, p/ ?! l" W; f( Z4 q2 X" i' }, u - $data['msg']='你已经领取过了,留点给别人吧!';3 V4 p5 |( R: t. s
- }else{
3 [' w9 G3 B8 K4 h& T+ ]7 n* v) p - //锁表
7 R' w1 w# A, D$ X0 i2 b& J - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');3 o9 ?' T$ u3 j7 u' f
- $active=M('active_num')->where(array('id'=>$id))->find();4 e1 s( J3 M9 ^4 ^8 M9 j- @
- if($active > 0){) n, o) u7 y3 @% ]7 V: u
- //开启事务
5 A0 S+ @6 I2 d- q3 g3 O3 `9 G - M()->execute('start transaction');
7 K8 E6 k9 D0 n. O" [9 `4 e - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));" Y9 n2 Z' V- i, L1 T
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));1 S6 M8 J; t5 ^! m" \8 S
- $members_preferential = M('members_preferential');
( w# N' i4 s9 |* h a - //对应投资金额,
+ P+ [+ F" E; M* Q' \ - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));; e1 V: E1 B/ U1 Y8 G6 O
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
?. ]8 x6 b/ [7 g2 @3 J) ? - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
% H h* @/ Y. L% K. | - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券7 s6 ?. Q6 r3 K/ x3 L
- if($save && $add && $add2){+ a' ?6 C$ O, l4 H- o
- //事务提交
7 S# Q6 F; l* r4 m) d - M()->execute('commit');3 }4 W. O* O6 @6 K3 Q
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
3 J; X1 b+ Z9 ~! D; l# [ - }else{" U6 o9 u9 r3 E; ^1 A
- //回滚5 ~9 D# v; x* S8 l1 k( T
- M()->execute('rollback');# X* ^6 p$ k* j
- $data['msg']='未知错误!';
7 z4 P# t1 J( l6 | - }7 ]: `, R2 e% D9 J* M' x; ~
- }else{
$ v' a }# e, u2 a- i# f6 N6 r- X - $data['msg']='红包已领完,你来晚了!';) `" Z8 @) b( g
- }
0 @- f8 ?$ {+ I- n9 x! I' c# ~ g" y - M()->execute('UNLOCK TABLES');- Z! V4 n, w3 U8 I! G" k
- }
% w# _7 B/ p. _7 N0 L( A, {* J - }& x7 M5 ^/ }+ j5 \# g: E+ P0 }
- }else{
% s( Y- K& \# N; ^( J' _$ x3 e0 Q - $data['msg']='非法操作!';
6 X/ ^: g7 ~% ?& k. @ - }
- u/ i0 k3 m- d. z - }else{2 \3 N* d2 e" `# w( @) j
- $data['msg']='还没有到活动时间,请晚点再来哟!!';7 I) F" M' B- [* Y" l: F/ F
- }4 u" o0 U- V* m/ U8 T2 _
- }
& X+ w& b: J' Y, }: T9 v% d, Q P - exit(json_encode($data));
4 r7 a: ?$ w, v8 C/ u - }
复制代码
( {5 P* a [. N& E
, {& h$ @+ I" M9 f3 ~. _2 e |