模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 4 F2 O: ]) v7 w4 H" m; o( r
Mysql中的锁语法:* x" H, } {! ^: l0 {0 f p
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
: o$ ?, v; r) k+ A9 }UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表8 F$ D9 @( ^3 v2 ]' d
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞4 U- z- J' P3 L5 V: X
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
* n. k- R9 r2 Q' y N, _. b文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
8 z" s) N* P+ c. v* R测试时,有个文件就行,叫什么名无所谓 总结:9 y% n4 b1 Q! x _0 q
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:0 {7 m2 e# [5 y; A- T: i
1. 高并发下单时,减库存量时要加锁
0 z) v( p. o! i/ [& ~# v$ O7 j2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
+ w( F. t s. E4 f j; j+ z - 模拟秒杀活动-- 商品100件& F# R. [- C+ p) O! o
- CREATE TABLE ta" c3 t% B0 M0 x/ v4 z
- (
7 y* L; B6 m* q0 I8 k - id int comment '模拟100件活动商品的数量'7 ~+ x* O9 z U0 ]$ E a9 y* t
- );) W8 [+ x! _- Z q- M+ q
- INSERT INTO ta VALUES(100);! p- u. \1 ?0 O
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件0 s$ k2 M2 j3 Q0 v, F* W$ m
- */ s6 ]) {9 ~* V s1 I2 o% v
- / u* n9 x" z3 N4 a n4 e6 t: ]
- // 关闭错误报告2 B7 N$ b4 S' h* G9 [
- error_reporting(0);
, f' {) B3 }5 }# g: H- K - 5 \- a. t) H+ t; x! E# C
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
# j5 y/ [/ O: l2 V* p+ a - $dbuser = 'root'; // mysql用户名
0 f+ x" @4 f* F- \ - $dbpass = 'root'; // mysql用户名密码; s! G* q; c# r! q% }
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);% w7 o/ e) i) V; u
- if(! $conn )
" M# P2 U/ C0 J! `% m0 P0 U+ R - {
" }. p$ k2 I' J4 v) m( A( ~ - die('连接失败: ' . mysqli_error($conn));
2 }, c- K' _* s - }
! |, p0 P6 ]0 ~0 y) V J6 X6 `; { - // 设置编码,防止中文乱码
- Y0 q6 T( {5 O$ f. ? - mysqli_query($conn , "set names utf8");! f+ s$ f U& f( L% v6 h6 {
- mysqli_select_db( $conn, 'temp' ); 1 ^0 r3 a% O! O3 M, p
- - y1 ~+ F" E4 s! f" y0 C
- # mysql 锁
* m1 g% p, P: @* o8 ` - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
5 F$ e6 E5 E# F) W1 T0 @ - $rs = mysqli_query($conn , 'SELECT id FROM a'); / i# u* @. j5 Z+ X( f! k6 p
- $id = mysqli_result($rs, 0, 0);
2 ~/ j. I( A: u - if($id > 0)
) W8 Y" _. Z) O) k8 ? - { : e5 M; |+ |. T
- --$id; 2 h. U$ g! S/ P+ R
- mysqli_query($conn , 'UPDATE a SET id='.$id);
5 x1 n f5 r; i, D- l - }
. J. y" x8 r9 g7 A6 N - * \3 P# ]3 Z7 K" w2 R7 B
- # mysql 解锁
% A1 L, B j4 \( y' }# m1 Z& p - mysqli_query($conn , 'UNLOCK TABLES');7 z4 a3 q5 R! e# A6 u' T+ _
- //查询解锁后的id值
! }2 o- \7 v$ c0 ` - // $res = mysqli_query($conn , 'SELECT id FROM ta');; u- _9 y' n8 ~
- // while($row = mysqli_fetch_assoc($res))2 z E" J( Q c% B, r3 O# |
- // {
* l6 m6 b3 e1 h6 ^( X - // $id = $row['id'];9 `1 \- P( g5 U8 {
- // }- ?! {' o! z) z4 E4 i
- // echo $id;
复制代码
9 L! T% A( Q5 x2 Q( m% d0 s, Z# x- ]6 k. [1 ~, I: q4 {
- |7 b% i+ _6 U1 X" m( X
PHP文件锁示例: - /*/ T# g! m, b* x; ~, H) R
- 模拟秒杀活动-- 商品100件
7 \6 _4 F: A) ` - CREATE TABLE ta
$ V% x" P/ _8 r3 D - (
% F: R6 _$ F# W - id int comment '模拟100件活动商品的数量'9 l" p' T$ {+ _. I+ t
- );0 j) N0 ~0 a# k3 d
- INSERT INTO ta VALUES(100);
4 q. h" x, h. \ - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
" g) n0 v& u2 A+ O( `$ c8 Q ] - */ g) x' N$ I- S1 _5 C
-
: @9 g8 O* o. d - // 关闭错误报告
! Z& W) [- Q4 o - error_reporting(0);
+ X; W) |. V) v$ b0 M; H% l4 o$ c - - Q0 c0 ~& U M5 o- @
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
0 w# Y, K7 i- S5 s- d - $dbuser = 'root'; // mysql用户名# {8 \. o0 Q" l, D
- $dbpass = 'root'; // mysql用户名密码
: n% }2 `$ S! Q l - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);& _# q% u7 s- V' |$ ]& L
- if(! $conn )& V" V* R o4 _% m6 }: ^
- {! b h; _6 G5 s+ D$ K# u2 d
- die('连接失败: ' . mysqli_error($conn));
n+ O8 z5 r5 A: B/ A Y - }9 U, y$ v- Q* u
- // 设置编码,防止中文乱码0 _5 Q' }/ i9 Y1 L: [
- mysqli_query($conn , "set names utf8");
9 `5 y5 n7 L$ D$ ? - mysqli_select_db( $conn, 'temp' );
* n# a" `0 Y; l' X - 0 P' F7 K2 N( f" ?1 ^
- # php中的文件锁
9 n9 j$ z2 q+ a( z4 u4 P# { - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 $ G% W) O/ Z- z* q/ N- R
- flock($fp, LOCK_EX);// 排他锁 5 y6 v6 J7 B& R2 ? ~
5 C5 ?1 `3 O* _5 H- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 9 y$ L7 l$ [$ E# O0 E H
- while($row = mysqli_fetch_assoc($retval))
. E7 j/ P3 f$ ^+ p& ~0 Z - {
' ^8 R" o+ N5 n" ? - $id = $row['id'];" j& j$ r1 c" r o0 C
- }
8 u2 J. L" W. z8 F2 X" r - * ? P- E: v- S$ }3 q
- if($id > 0)
, ` b, _7 p# n# y4 W1 G - {
+ ~" C8 ]" N& m, i) H& w - --$id;
/ h' ^5 Z2 w* g, F# x: [ - mysqli_query($conn ,'UPDATE ta SET id='.$id); 8 v$ W3 ?2 B. ^+ I5 }# q. {
- }
4 B. a" T- v6 c2 d - # php的文件锁,释放锁
# w" x8 V& n% U) C- |8 P6 T; {) {4 [ - flock($fp, LOCK_UN); 9 _& ]4 Y% h' J- P# x9 a
- fclose($fp);/ p) \3 F! L/ g: A! g
% U$ E3 {1 y% I6 L; w% z- // $res = mysqli_query($conn , 'SELECT id FROM ta');
+ e' X9 r, ~7 x& S; o7 c - // while($row = mysqli_fetch_assoc($res))
, Q/ R E' S5 {/ ^, U9 w - // {
5 P0 y% Q! {2 H+ ` - // $id = $row['id'];
S$ v y1 O, G3 }, R: D; ? - // }6 c" A$ t! f6 ^$ F
- // echo $id;
复制代码
/ J" Z6 f" Z8 G _* U! A7 m- Z8 t
抢券活动实例: - public function envelopeSnatching(){
4 i+ B5 y; W: J& I - $lingqu = $_POST['type'];+ b/ Z" b" E1 w7 j+ z
- $uid=session('u_id');//用户id
5 O. e/ r& |% T" W! q% P* f - if(!$uid){
3 o5 h8 U% y9 | j' v - $data['msg']='您没登录,请先登录!';
$ f* Q/ c- L0 b2 @9 ?8 u- A G0 ^ - }else if(date('Y-m-d') != '2017-12-12'){" J: a/ x1 c2 @* s! A
- $data['msg']='不在活动时间内!';! T/ A6 d: {6 F
- }else{; f4 D/ ?4 t" _0 C
- $hours=date('H');//当前小时数
9 {2 K; X8 I( B4 G9 M1 {4 m - if($hours > '09' || $hours > '17'){8 ?$ h! c( Z8 B* U; ^
/ G$ z( e# v' {4 j7 ^* l, K- if($lingqu == 1 || $lingqu ==2){//点击10点的2 u7 W1 ?+ S" d- x% u8 L
- if($lingqu == 1){
# e7 I5 W5 y( s# a& S! k - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过( |9 A4 Z+ F, q+ z. \/ e" x% `, @
- if($hours > '09'){6 O7 u2 y' H* l3 C# A- T( Y; J' m
- $num=mt_rand(25,28);//优惠券金额
0 ^# }. a/ N" x2 y5 z - $id=1;. Z+ Q' n$ ]7 L$ l
- }
5 L* U$ f& I. g/ Q7 G9 U& ]+ y+ a$ m - }else if($lingqu == 2){8 ~/ D1 x6 B5 w t. p% l
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
9 W9 E) F' k" ^+ U* l7 j - if($hours > '17'){! `3 ~4 y3 Y4 L( h- |2 U) L
- $num=mt_rand(50,55);//优惠券金额$ {- D2 V7 I* N3 v
- $id=2;, p9 O% y: i& K- Y
- }6 Y6 v9 U1 T; v% N: B+ @0 L
- }
" _) ^ }, [ m; t) | - if(!$id){
/ U, _ E, c7 d: c2 N - $data['msg']='时间还没到,晚点再来吧。';
5 V7 D) J3 ]4 s q+ D% c - }else{
. G% G8 C" M9 K! Y! z1 S - if($is_lingqu){1 m2 E- B" {& ~8 u0 e8 ?4 M2 y0 j
- $data['msg']='你已经领取过了,留点给别人吧!';
6 U# e1 u# o c& j" X) H - }else{5 z9 M. `- O1 k6 c) ?. b7 J
- //锁表) s' U7 a- X. A, p
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');1 k% a3 F/ ^* }* D
- $active=M('active_num')->where(array('id'=>$id))->find();
+ q& C, l! T& Q6 g - if($active > 0){
3 I% [# G! V: z - //开启事务/ a/ |+ O% D$ W P4 l
- M()->execute('start transaction');3 u" {. g8 g3 S. X+ y
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));: }) P# v1 h/ W& [. t+ K
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));% B- n2 {6 z5 `* `8 y' k
- $members_preferential = M('members_preferential');
: w; ^' S( C; m+ j% L0 B - //对应投资金额,, D! ?- r: z( _4 K9 l
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));; c1 N5 [0 u% o$ o9 t% ?# D3 b$ R
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
0 J4 n" m1 E, B" r8 c. B - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
. j3 H9 H% E8 ]5 I - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券% u& Z/ r0 {; t, \# ~( B- G3 n
- if($save && $add && $add2){
3 K5 m4 ]& j4 o [7 ? - //事务提交
8 L4 ^& p7 B2 e2 d8 Q - M()->execute('commit');
2 ?$ e3 `5 W# q4 u+ o! ? - $data['msg']='恭喜你领取了'.$num.'元优惠券';
6 `: w a% o+ X4 e3 a' u. R: W: q6 e" ^ - }else{* V+ D2 Z* Q- w& ~4 g) p
- //回滚/ x5 q! r9 @" s+ K. |$ q8 w
- M()->execute('rollback');
* `0 d* q- w! b7 Z' e - $data['msg']='未知错误!';' G1 G" `3 F, ~$ Q) n
- }
% ?/ [" c- T9 n' ?8 \& w& A% E# W0 u! G - }else{' J. t' a0 D; B( s8 p8 @
- $data['msg']='红包已领完,你来晚了!';: D; b$ _) K9 r
- }; [/ |& K5 r& F2 [- S" z- d8 k
- M()->execute('UNLOCK TABLES');) L& w% `0 F: P3 e3 n' c4 |
- }, l( Q, F: Q5 i0 g: j
- }9 `- j: P! v) q) O: ]5 d
- }else{" L/ { A' h/ Z f w3 @+ v
- $data['msg']='非法操作!';' b9 G7 H1 I7 a. e! x/ f
- }
2 r$ y" A. q9 m S3 _0 X/ e8 q3 K6 I - }else{
) D/ S8 J I* b6 e! r - $data['msg']='还没有到活动时间,请晚点再来哟!!';
. ]0 a/ `8 H" ^: K3 [% E# u9 E) X - }2 F4 a1 e$ [. z1 P
- }
+ ^& p( L2 O/ q8 g; p& t - exit(json_encode($data));+ y/ C3 O) y9 [* q1 q/ y1 o/ f8 q
- }
复制代码 " F/ h0 l" t- v
/ P% R. z5 c) v6 F) y; f+ ~ |