模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
# d; X6 p- o4 Q- i( B; e" lMysql中的锁语法:& D3 z0 S5 a; g% |+ M2 T
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】" a' G/ u0 ~+ J: \
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表: d9 Y* @2 q$ _. v* p
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞# B2 Z! ^. e; ~2 f: e1 m" R
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 : b! O' @5 t8 }" T" N
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
* H+ I0 Z+ k2 W9 }+ G: s测试时,有个文件就行,叫什么名无所谓 总结:
8 a: S+ a+ R7 J项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:1 p% V, X0 c9 k
1. 高并发下单时,减库存量时要加锁
W' t$ [, G S ^2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*2 o# F' [9 o3 J0 |5 n4 r; Y/ D
- 模拟秒杀活动-- 商品100件
0 N1 X! p% a) J$ r. B: ~6 y - CREATE TABLE ta# Y P" t1 @9 Q2 @" ]
- (
4 v8 ^3 I: W0 Y/ v+ j; k - id int comment '模拟100件活动商品的数量'
8 K' Q8 Y2 q- v - );2 P! G5 x5 s6 |2 L
- INSERT INTO ta VALUES(100);
/ Y% u9 W0 U8 _/ P; P - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
( U- `. g* i/ Y+ E/ _( H# [) }( T. M - */ 6 Q$ R4 Y( n$ p- m; ]
-
) O/ f0 ?. M" Q) t- M! y - // 关闭错误报告
1 b3 N' c+ {6 J. ^5 i% [; p - error_reporting(0);
" ] _" D# Q3 h+ o; C$ M8 }* y
4 H% B9 q* O5 |% h- $dbhost = 'localhost:3306'; // mysql服务器主机地址" {& `) F1 w, \3 J" F1 Y& _% O
- $dbuser = 'root'; // mysql用户名
. f$ ~! a6 g6 O! E: R3 @; ? G& C - $dbpass = 'root'; // mysql用户名密码$ \/ q' Y: O( g- ^8 `6 t
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
0 o7 o! b$ z: l8 ^! D' B - if(! $conn )& V* Q0 e8 p% h+ `
- {2 _6 u4 `/ G$ U8 r$ R
- die('连接失败: ' . mysqli_error($conn));
0 H% s" r( h. p: W - }
$ t% ]9 `+ X% Z. Z* b - // 设置编码,防止中文乱码) r* _, u4 X! p5 J
- mysqli_query($conn , "set names utf8");
9 G# M- B8 L* k2 x6 t$ Q - mysqli_select_db( $conn, 'temp' ); 6 f/ F2 b" j' }9 M% X
* N& `4 b6 h6 E$ z8 w. i. N- # mysql 锁 $ l( ^' N: k+ {1 n
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
; l1 P) r, |$ M - $rs = mysqli_query($conn , 'SELECT id FROM a');
% D0 y/ m) D4 z5 S% T - $id = mysqli_result($rs, 0, 0); - [, R$ y- D5 S/ Z* P) S( U5 L
- if($id > 0)
% e# d$ {1 x9 q - {
" O+ Q6 ^4 ~) i - --$id; + S. W7 b5 f2 [' s* a
- mysqli_query($conn , 'UPDATE a SET id='.$id); - Q& f0 ^$ m, h4 \, b
- } . p% A" G' ?- Q/ w! m% ]
- ) G) g, \) f5 o; p
- # mysql 解锁 * \2 K; O1 v+ o, |- M! I+ Z
- mysqli_query($conn , 'UNLOCK TABLES');
/ J n- i9 a" c% y" o/ H& [ T - //查询解锁后的id值
8 b6 H& r) x3 h" Y - // $res = mysqli_query($conn , 'SELECT id FROM ta');5 o: j4 x1 `# g4 F; i
- // while($row = mysqli_fetch_assoc($res))& Y( y: H$ r* j V# u9 C. t6 l
- // {. @1 Z/ Q7 `: w+ c# j; _* o
- // $id = $row['id'];
0 ]' G( l/ m) {# q1 P - // }
- N6 B& `! j. v- r1 O - // echo $id;
复制代码
& L: ^! N6 j6 A0 {1 Q# a( N) m
0 l1 S- \- e! i/ g' d& ^
+ u" X- E, Y$ fPHP文件锁示例: - /*8 v( W3 r" S1 d1 y* ^
- 模拟秒杀活动-- 商品100件. S+ @7 M' W; _5 B
- CREATE TABLE ta7 C% s$ y, n( c: }( q$ I
- (1 k; b! J& C! v0 i0 N5 H- ^7 Y3 ^
- id int comment '模拟100件活动商品的数量'
# t8 ]! b' E' U; w& ~ - );
+ Y) u; ^1 L! I2 B+ s - INSERT INTO ta VALUES(100);% l. b, s h2 v- F
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
. Y2 P& J7 F$ j - */
3 U5 E) u5 U) R2 Y( m$ {! x -
3 O$ } @6 o6 @9 `! f - // 关闭错误报告
/ E* P( z7 ?5 O. d/ `' f - error_reporting(0); 0 Q5 n- M2 F" ?2 g/ p3 ^! v/ w" r
- 7 O, b! `/ q7 M# {
- $dbhost = 'localhost:3306'; // mysql服务器主机地址1 I/ ]' b2 d1 J' X( _3 ]; N* n
- $dbuser = 'root'; // mysql用户名
8 f) _! T6 `* p- b! y! g - $dbpass = 'root'; // mysql用户名密码& O4 N. j$ L/ O/ W, n; r) |5 B D! U
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);9 V: ^) J# U% t$ w
- if(! $conn )
+ D- X$ _ v9 }: |: H; z - {4 o1 S( D+ f( u" i0 K
- die('连接失败: ' . mysqli_error($conn));
1 |- Q) @* ^. e/ i" K1 a/ g+ a6 x, T5 u$ X - }4 Z6 d. @& I6 A: g( p
- // 设置编码,防止中文乱码2 @6 s& _# e$ N H3 R- a
- mysqli_query($conn , "set names utf8");% m5 G. p+ \( x$ `! o# B, d3 G, l* Y
- mysqli_select_db( $conn, 'temp' );
( ~6 D9 b0 j0 L1 Q) F - ' f1 ^( }& W4 m3 U8 @+ l/ T
- # php中的文件锁
$ l! K8 B0 ?) }6 z, M) ~ - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
6 o, ~& v- }7 N - flock($fp, LOCK_EX);// 排他锁
7 P E. ?7 f8 i - . \. z: b% Y; q; A" Z, k! h
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); , l. Q; w& |* K6 Y' f1 d
- while($row = mysqli_fetch_assoc($retval))
9 ? L1 X; E3 i5 F1 B' X - {
p0 k& \8 P6 S* w - $id = $row['id'];
0 i* b# ` s1 C8 n2 G* m+ q8 [- w - }% ?" X( X% J0 l k
- + B5 u7 _/ b! z/ ?0 X
- if($id > 0) . L& D$ b. ?$ F, B M' n
- {
8 {! Y# M# v) N6 ~7 d1 w$ ^ - --$id; # ^4 \' q( `0 Y: ?1 x
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
3 ], E- s* J6 { - } + p0 ~/ L, B3 N# H; _/ N
- # php的文件锁,释放锁
4 z( t8 A6 T: H' r8 f* K( { - flock($fp, LOCK_UN);
( ]9 z! W8 r, Q6 r - fclose($fp);' M. a) }1 `* Q$ D+ f D
Q4 H; u" r0 E! S- // $res = mysqli_query($conn , 'SELECT id FROM ta');
6 \' Y3 D2 F. M1 Y4 @ - // while($row = mysqli_fetch_assoc($res))
/ S* {- H( I) C1 ` - // {/ }" b6 g% J$ H& }) [
- // $id = $row['id'];
# ~8 d1 a: Q- {4 k( l - // }
9 F* S. F1 H- F4 }! N4 U* S - // echo $id;
复制代码 " j7 n a' P! A' V- y
* N$ j# ?) [5 @2 o1 v$ a/ d
抢券活动实例: - public function envelopeSnatching(){
: g0 |! W6 n' |, y- ]6 T8 i$ Q4 U - $lingqu = $_POST['type'];
2 \7 [+ g3 n1 Q: a" v& X - $uid=session('u_id');//用户id* t% g+ P* J9 L/ `: F% i. r* W
- if(!$uid){
, a ?% B* O$ a- J9 F9 b - $data['msg']='您没登录,请先登录!';% B$ R. n. p/ z: j5 r+ [
- }else if(date('Y-m-d') != '2017-12-12'){2 S. x( T8 `" F4 y% y7 {! W0 p
- $data['msg']='不在活动时间内!';
/ J, f& V- x# G0 b - }else{
, s% n( X+ o9 P/ ]$ M, }& J - $hours=date('H');//当前小时数* K& {% u/ ~5 ]. P
- if($hours > '09' || $hours > '17'){3 ?" S1 G5 B# I+ C' H' z
2 \( K. i. I- A3 W3 z* f- if($lingqu == 1 || $lingqu ==2){//点击10点的5 b3 X5 s3 D- Z% R0 X1 T2 U, u) @
- if($lingqu == 1){
- w4 G1 c8 M% u7 e3 n" D - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
( S3 o/ d9 w# A5 a - if($hours > '09'){3 y5 O6 Z8 t9 U, }) N: ^4 v
- $num=mt_rand(25,28);//优惠券金额
! d5 K& n* o& U7 r - $id=1;7 ^& K* q) l# C. X
- }
: }' p3 h+ f* o4 Z% P: k$ B7 r! ~7 o - }else if($lingqu == 2){
. S- p* W+ _: g0 d/ r; | - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
4 `4 _$ G8 M0 w. R/ ~ - if($hours > '17'){) F& K5 i9 J" }) Q$ F7 i
- $num=mt_rand(50,55);//优惠券金额
9 K/ _8 E% N. ]4 H( P - $id=2;
% X8 c/ m2 L0 y: T4 C - }
$ ?. p1 V: B* o5 H& |& k$ a - }
~/ k* a) P$ S! X - if(!$id){
& Q6 A1 K8 q8 w" R/ v - $data['msg']='时间还没到,晚点再来吧。';
- Y1 o( [3 S1 A+ L8 f) d9 X - }else{
# V, k) o4 d5 |9 L, U' \+ c: H" r - if($is_lingqu){
8 g4 F* w, A0 N - $data['msg']='你已经领取过了,留点给别人吧!';
6 F7 |6 l! s# j; ?5 ]9 z - }else{
# c+ k& B1 y( Q* j) [; C. h - //锁表
$ x# F# A% C8 O- k - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');, C* M0 B" X( L! p9 z
- $active=M('active_num')->where(array('id'=>$id))->find();
; p. [6 d1 ^# F - if($active > 0){
2 T# V$ Y! w r6 i7 _' r7 `, @ - //开启事务8 s; L- x& B d# _" U, x
- M()->execute('start transaction');
5 I7 m6 B# r, M+ }( o/ G - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));) @1 Z% c" [5 j$ r3 d) s
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));0 E7 h1 I* n+ b- L% c. C
- $members_preferential = M('members_preferential');
- U% q$ ^, C$ L& W9 L - //对应投资金额,
% z, r0 S. S7 u - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
' D* A+ z2 b. _2 { - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
W, S- o' H+ ~; k6 C5 [$ @* H; I0 M - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
& G2 E. U5 X( S( L, N/ n; ^) L2 t - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
: e5 P" t, {* m7 P% ]. { - if($save && $add && $add2){
( D7 p9 h% I1 s9 _! [/ ?: X6 m" [% S - //事务提交3 Z5 V+ B6 E, m! g' J/ k1 W2 Q
- M()->execute('commit');
! [: ^4 E6 ^; J$ q5 I - $data['msg']='恭喜你领取了'.$num.'元优惠券';3 k; W0 u" ~, H3 b
- }else{
1 Y& A& T( L; S* o4 s! l; |, d, R - //回滚
7 _# q* n4 s! J8 A# e! W& c5 M4 g - M()->execute('rollback');/ I) W* l0 J* `$ O, Z
- $data['msg']='未知错误!';
' I9 g( H. P2 K. z+ L; m6 [0 h - }" K9 X3 [% i# X4 ?
- }else{
5 P5 {. S5 l5 M, @2 t. K - $data['msg']='红包已领完,你来晚了!';1 j }1 k* D* m% [7 c
- }( s1 F/ D5 I% l0 X$ F
- M()->execute('UNLOCK TABLES');: p8 i% Q7 G( O5 `; B3 [" {
- }8 {% r& j k: i9 C" S8 g
- }
- q6 A% b* U. r6 M; H; f - }else{
. \1 Y# _, _) G# d1 c- W - $data['msg']='非法操作!';
; k" C6 N% r% T' Q. {3 Y - }$ Q$ C. j! ^8 W5 S) n
- }else{2 j6 O w8 C( u2 J: {; | Y
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
/ u; L0 i- O, X. d! m: n5 a# [ - }
* l8 u5 K2 F* X2 k' F, {; ^/ c - }- K6 T8 I! q7 |, h9 f; X: _% o* h
- exit(json_encode($data));
. \" O4 n5 n! B) |$ W - }
复制代码
$ t- S$ `) F* O3 t' G; W; {. f8 a% \& r' l9 H3 A o/ n; o
|