|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
& K' f: S: o3 ~4 l* gMysql中的锁语法:3 C# C2 q# [& F3 z$ q7 v
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
# L; g( G8 b U9 C& ^8 fUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
& i L0 X" g9 v# O! [6 QWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞) a; l# X; ~/ E# G! Q% x
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :/ |! m9 ]* a& i: Z% f( z9 {
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。' U+ M. }3 |4 t
测试时,有个文件就行,叫什么名无所谓 总结:
: I$ A# m+ X, a" ^, ^8 n* U项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:" g4 R5 X4 F) V& \) H9 F3 g
1. 高并发下单时,减库存量时要加锁
5 B/ f2 q) V; S+ x( }0 ?% g2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*; ~* Z, p* O) U& `4 y5 v8 S
- 模拟秒杀活动-- 商品100件" p8 U/ G v( _8 O( }0 i
- CREATE TABLE ta( u: Z( @" ?: q, ~' ~
- (
( G; u. [3 C' J M$ j8 Y9 F4 c - id int comment '模拟100件活动商品的数量'. x: g5 m+ j% b, H& Y& ^' M
- );
& n9 K$ H O5 @. V! I - INSERT INTO ta VALUES(100);
* }# {" W+ M$ f- U& S( I6 ?8 K - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件/ M/ Z5 l, o: \
- */ 8 x4 J( G5 O3 Q2 H7 H
- ; }# }- V% U7 @: M9 J) {
- // 关闭错误报告8 C/ Y0 H' r+ L9 [# D) B7 u
- error_reporting(0);
3 S) L2 c$ v; e. P$ o - + O5 }8 s/ d* u% a
- $dbhost = 'localhost:3306'; // mysql服务器主机地址% C7 h* j9 N9 w R) c
- $dbuser = 'root'; // mysql用户名/ `, c- @% B# }' s
- $dbpass = 'root'; // mysql用户名密码* ]2 I; p4 L0 X2 d6 b% w& V
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);4 B; X; o: Z1 I; a9 M5 p8 Y
- if(! $conn )# X# i0 S% b0 Z6 D, B+ c: V
- {
6 [1 G; J: U3 p# \. J# P - die('连接失败: ' . mysqli_error($conn));
8 N/ ?4 L. _. T3 L - }
8 [% p2 [( F+ x, T9 r - // 设置编码,防止中文乱码
! ]$ D" n/ J. n! l - mysqli_query($conn , "set names utf8");& o ]6 s ~ J2 [
- mysqli_select_db( $conn, 'temp' );
7 E t: I, M9 v5 I- C0 m# u$ L
! y' H% p# y2 I9 |9 q3 M$ n- # mysql 锁 2 K1 ~% y# n7 F- ?4 S
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 + o! l6 F' N1 L4 j; x
- $rs = mysqli_query($conn , 'SELECT id FROM a'); 6 E. U" O, K* o+ Z
- $id = mysqli_result($rs, 0, 0);
# E% s, L( e8 U3 f3 F; ~5 e4 [ - if($id > 0) 6 m% `4 K+ ^% L: ^: M! d
- {
' N3 t* z$ A* S4 |2 d - --$id; ) ]6 m0 s+ L/ k' l5 I6 L, N
- mysqli_query($conn , 'UPDATE a SET id='.$id); , {1 g( z7 K" i& \: h8 O( u6 ?
- } 6 Z. u7 ^" i0 u+ }
0 ]0 N+ P( o9 Q- # mysql 解锁
) T' v8 M5 l+ j6 ~$ s J0 U) g3 W - mysqli_query($conn , 'UNLOCK TABLES');
6 R# @* M# @& Q4 v - //查询解锁后的id值8 K# c( O# U" Q; b
- // $res = mysqli_query($conn , 'SELECT id FROM ta');: D8 S4 P" U0 W5 \/ U0 @2 \
- // while($row = mysqli_fetch_assoc($res))7 l4 E Q0 m/ {, _) b9 V
- // {; D1 d: g, {* r/ {, p
- // $id = $row['id'];' X; U# t! I- p+ k, u
- // }
f9 A9 Z+ s2 `' D8 G4 u - // echo $id;
复制代码
. r- Q/ Q/ y6 o. }
( M W5 `% z8 f2 V- ^% [5 Y) G% Y7 b
PHP文件锁示例: - /*; P; U0 ]$ O! Y$ X6 D
- 模拟秒杀活动-- 商品100件0 z0 k6 H0 ?+ t. |5 G5 o
- CREATE TABLE ta
! T$ L7 L- c& r8 ~, ~4 M) D7 W; x; u- y - (
6 ~$ [' g2 u; B Y - id int comment '模拟100件活动商品的数量'
6 Q2 n& r; w. e( a - );
% f G D# u {$ Y* i6 E- ~7 `( f, d - INSERT INTO ta VALUES(100);& R' Y$ v& v _& ?
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
# h1 [: p0 H- Q6 N G$ ?7 R5 A - */ ! K9 r* B8 Y5 [0 z, m; J$ {* e
-
. B# N5 G, G0 Q- }! h* a6 `+ B - // 关闭错误报告! b! P' ?7 k& u9 B. n6 T; o$ k
- error_reporting(0);
9 E; ^( G! ?0 z1 ^* X% | - / T4 l, X) {' y4 a2 _9 L9 O
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
" r' a$ @6 }4 B; _, ~4 r - $dbuser = 'root'; // mysql用户名& c. B+ ?3 Q. ?1 N% U; |& |
- $dbpass = 'root'; // mysql用户名密码4 C7 l e( Z) b6 \$ ~! ^& u8 C
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);( N2 g* O% K+ D( s& E e7 a: M
- if(! $conn )
4 }% S1 D0 G7 X) @ - {- i& u) l4 u. |* N- o6 L1 J* h4 M
- die('连接失败: ' . mysqli_error($conn));$ o' E b2 w3 h" Q. ~ R. ?* X
- }8 j. C/ J% T- d) m+ @* Z
- // 设置编码,防止中文乱码
: k- }+ u& Z# ^# v! `+ |2 S ?+ r - mysqli_query($conn , "set names utf8");
$ C* B- i, }8 D4 W4 u c { - mysqli_select_db( $conn, 'temp' );
7 o0 ^& x; G, n4 F, b+ } - + K# B, K: ], ?4 \ K
- # php中的文件锁
: b/ [/ w# \, Q# B5 p - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 1 Z) E2 ?) \3 ~' N
- flock($fp, LOCK_EX);// 排他锁
% X% w$ i; ~3 G: r; g" y - 8 z6 @) U7 ^7 f; e1 t' h
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
% V" D8 f y, B& s5 d2 ^" { - while($row = mysqli_fetch_assoc($retval))# h( _. K8 r! s+ w+ y' w, ]9 ?. L
- {; y" C) c1 f6 { q# M9 `+ H' I [
- $id = $row['id'];
2 n" u& N2 M; { - }1 v# X9 Q' Y* w+ X
-
0 B8 O" r& z( [ - if($id > 0) ( E J' @/ A' j
- {
5 c4 d6 K0 U9 D8 j7 w) f$ ]$ u* p - --$id;
+ J R6 d5 c# r - mysqli_query($conn ,'UPDATE ta SET id='.$id); & T( I$ k, t% I- ~
- }
H$ u H9 m& C1 T& } W) K P - # php的文件锁,释放锁
9 e% a+ h. p# F! M$ G* F% w. z - flock($fp, LOCK_UN); 4 C& v0 |- v) N7 b, l& T
- fclose($fp);
+ j# [+ Z# f% v) o, K4 D" E - 3 x5 B; t7 s8 W0 X& o* f
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
E, X: k r1 E: Z' U0 a - // while($row = mysqli_fetch_assoc($res))
) x& A. u; G, ]* z# N0 D* M - // {
4 Z9 z% A( l& ]$ v - // $id = $row['id'];
, }# W/ O& t8 n! U; l# ~8 ]& T - // }
# G: E6 K5 X: x% S: ^9 T, z - // echo $id;
复制代码 $ k1 K; O0 t. X4 V
5 D# S- f" P2 t$ p9 N2 m' w
抢券活动实例: - public function envelopeSnatching(){5 A% L+ C- S% ~$ K
- $lingqu = $_POST['type'];9 G* {1 Y" v7 L$ U
- $uid=session('u_id');//用户id! H9 }% U5 T: W
- if(!$uid){
3 x* b5 }# K$ e/ M! L4 D t - $data['msg']='您没登录,请先登录!';
1 B' F* d8 Y B+ I e7 H& l - }else if(date('Y-m-d') != '2017-12-12'){$ U% I1 ^# @, ~ w5 W# O0 |+ n
- $data['msg']='不在活动时间内!';
7 B r1 B+ L: h2 y% Y9 P4 ~% F. g' L - }else{& u4 ^. _+ y4 }! {
- $hours=date('H');//当前小时数
' Q6 M9 v A5 `9 `& o- e/ J - if($hours > '09' || $hours > '17'){
) q% s9 }, B& I! t
" O* F/ y$ c0 _2 x7 X) I# f- if($lingqu == 1 || $lingqu ==2){//点击10点的
$ Q' ~" p+ Z8 ~8 N - if($lingqu == 1){' E5 P3 |7 \" I& h
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过( E4 L3 n$ F% w9 r: ^6 C
- if($hours > '09'){
/ E2 i! v/ K3 i0 Z2 r2 U4 P - $num=mt_rand(25,28);//优惠券金额
! Y; i+ i) g# _: c# [6 ~% R - $id=1;
$ `. u7 v2 _5 Z - }+ N2 u& K8 U& ]
- }else if($lingqu == 2){ z- X3 e$ m) S& p' a# J0 _2 K
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();9 {4 K5 z2 B; o" h8 o0 O
- if($hours > '17'){% C$ @; e) I( F* _% W
- $num=mt_rand(50,55);//优惠券金额
# h' u. w/ j/ [4 K4 H - $id=2;
* p6 j. ^6 O/ ~2 @7 T7 X6 G; z+ C - }8 ]+ o3 s! z+ Y+ V0 |
- }9 }$ L7 I x' {
- if(!$id){
2 n! @* ?% \9 A) m2 U8 q - $data['msg']='时间还没到,晚点再来吧。';
* p* v; }4 |7 q3 J6 T1 G. T/ W' Q ? - }else{
/ I. f- x' }" D5 j: l9 @6 N" ] - if($is_lingqu){0 v' R' d% }4 i, ]8 l
- $data['msg']='你已经领取过了,留点给别人吧!';7 R: U# r" B9 b* A4 \$ y
- }else{
4 d5 N/ W4 T' V( `' G* o - //锁表, D; m$ P% K" R$ L5 ^% p4 w7 F; @
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
$ |. T* R2 d' V: P2 } - $active=M('active_num')->where(array('id'=>$id))->find();
+ ~' A B& H9 M& N$ Z( m( _ - if($active > 0){
! v+ J/ c3 w4 I4 Q - //开启事务7 i% l+ z( Y( ]# T3 o
- M()->execute('start transaction');4 f% d5 Z4 w, Q l% Y4 @
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
( f& E* O' ?4 G/ L0 f - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));' W6 f. l6 l! Y" v0 q: t7 y$ j- x
- $members_preferential = M('members_preferential');+ a8 q" _$ x C' I4 U; f
- //对应投资金额,' A; J8 a3 ^5 M! L3 B
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
7 \* n, `6 x9 }' o8 s3 C - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
2 L$ r% R6 u9 t: `" O3 p9 C - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
9 [' c/ i5 j3 `4 @, ^3 V - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
+ z( O1 Y$ B* v- \ - if($save && $add && $add2){( B/ L% F* Y' v/ L K, z. v
- //事务提交1 `1 n9 q- F! _) p6 D
- M()->execute('commit');0 h+ V- \( N. A) Z) U+ S" O7 B
- $data['msg']='恭喜你领取了'.$num.'元优惠券';4 B% x1 c% W1 m# N1 T
- }else{
3 u6 g8 ?9 A$ {2 _; W t - //回滚( i) d% |" R- z5 M
- M()->execute('rollback');
0 R) C9 Y' q! j& Z1 h8 F, ?- A3 O) s - $data['msg']='未知错误!';' ]5 _# d# b3 E* p$ s: v
- }
/ V# _; I" t B0 V' S F9 |/ z% ?: l - }else{& {/ b- J9 }( g; J4 n( r- Z
- $data['msg']='红包已领完,你来晚了!';
8 {" g7 m! q- V X3 w# ~ - }( M. I# M9 ?' K# P, D& `" {+ D3 m
- M()->execute('UNLOCK TABLES');, M4 i0 i' D4 z5 q# u3 R# i* A# Z j
- }
% L8 _% N# @5 Y - }* d& D2 j- B- {+ C( V
- }else{7 i& w+ N9 Z4 i5 v6 M- p5 e
- $data['msg']='非法操作!';
8 K0 [4 t4 K# U8 O - }" x0 h4 a! F3 {& s# G
- }else{5 U% T, a/ h% G
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
% @" L V6 Y" P- Z1 b8 c - }6 X5 A! q, G# E, |
- }
0 U# T$ Q+ n, \% u, M7 h3 _( X; A - exit(json_encode($data));
! v% U' ^: P9 d, r6 U6 Z% F - }
复制代码 6 c, b% X7 e7 W$ J
# l& a( s4 L1 k# {0 P: s0 r+ q+ m! W% X' G
|