|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 % {3 r0 u8 c/ X+ H* M
Mysql中的锁语法:
4 c( N8 k- O( E* ~( nLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
1 G l, f# P3 s8 z- h5 KUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表7 i1 h2 ^0 B& R9 [+ p6 @$ ~. h% ~$ Q
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞$ E# Y# W3 r: V1 G' Y% Z. z
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :0 n1 ^4 n8 Q$ w: A3 ~/ L f
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。 D; M5 V* V- z
测试时,有个文件就行,叫什么名无所谓 总结:9 _4 v$ ]/ T3 L9 ~- e
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
( ^ w2 I6 j5 o" n1. 高并发下单时,减库存量时要加锁
2 F: `* {$ a5 u2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
1 U7 J0 s& j7 t1 E - 模拟秒杀活动-- 商品100件& L9 |. {" c$ ^9 v) j1 o D
- CREATE TABLE ta" U9 w1 H( p, }/ ~, P# d5 v
- (5 D" m6 t3 H: |
- id int comment '模拟100件活动商品的数量'
q% h6 v7 _4 Y9 b6 u - );
5 ?' c7 Y" [! Y/ J8 p9 n - INSERT INTO ta VALUES(100);
1 j" @7 B" [9 a2 K8 p - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
( } l" w4 r, c2 h2 e) P- h - */
' I3 { k& G. B1 @* f( ^+ }0 z$ b - - B' y$ D i8 e6 q! S
- // 关闭错误报告3 ]4 O3 m3 ^4 _* o3 G
- error_reporting(0);
* `# q( m8 T: e& ^3 D7 H - ) k4 Z1 v+ o& y$ F! d7 s% V
- $dbhost = 'localhost:3306'; // mysql服务器主机地址# \+ R3 \- e; T) D3 x
- $dbuser = 'root'; // mysql用户名5 `) O/ |' \+ o$ v. A
- $dbpass = 'root'; // mysql用户名密码
2 V; D( B. J5 u- `9 n - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
1 G# y9 o2 e" C5 S' m# W - if(! $conn ), ~- M0 D" p1 b! s" W
- {
9 e* Y3 \$ I" s5 V1 ^; G+ Z - die('连接失败: ' . mysqli_error($conn));& I9 @3 q: {# F1 T$ W4 B0 |, {
- }
. W, f% I" }. _. |% W - // 设置编码,防止中文乱码, N6 Z! ?4 c# U# `2 I/ P Q. e
- mysqli_query($conn , "set names utf8");
4 ]$ q0 U4 D2 Q, U - mysqli_select_db( $conn, 'temp' ); - C, ?; j H7 P* K; q8 u+ r
- # e. h) w9 f" L- n8 F3 E7 d. R
- # mysql 锁
( ?+ h+ J" x F8 ]" D/ V' { - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
" U* B. p3 G5 L# f% d( m - $rs = mysqli_query($conn , 'SELECT id FROM a');
2 X% C7 b" V4 I; o# X - $id = mysqli_result($rs, 0, 0);
+ p# z4 ~; n- ?- D/ |! f4 l - if($id > 0)
6 E0 q, c5 Y$ k - { 0 u$ t# x2 `! U" g/ y* i5 N. {
- --$id;
! f' N9 i# _1 k- I7 F9 s g - mysqli_query($conn , 'UPDATE a SET id='.$id);
' ~1 e# j+ r( r6 a - }
$ s8 r6 c ?8 L5 S# k- w* K" l8 R - : D# Y- v3 p; x D# s# b
- # mysql 解锁 , D+ `9 x: q. Y
- mysqli_query($conn , 'UNLOCK TABLES');
/ a: V/ D& \6 E0 @! a, J7 E- Q- Z1 F - //查询解锁后的id值) \' I+ d1 ]% `) Q" y! O5 _2 T
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
{$ N# Y) c/ q* V& X/ [2 f - // while($row = mysqli_fetch_assoc($res))
% w* m" Z% h% n% d - // {4 i- m3 ~4 @$ Z5 [% A8 \
- // $id = $row['id'];
0 d$ h% z1 t. |/ k - // }5 c! I; N! D* ~& Y9 m/ o+ C9 P
- // echo $id;
复制代码 , m$ S' S9 J# T3 k
3 K* X: c& J! ]; s/ j
9 @ ]6 V4 @" iPHP文件锁示例: - /*" q# Y5 [% d( @8 g2 f0 \
- 模拟秒杀活动-- 商品100件& f( y. I) _' p+ P+ l9 G
- CREATE TABLE ta
+ l0 ~' A* {1 Y& E - (+ q! S5 g* P# k: Z) {. x4 z
- id int comment '模拟100件活动商品的数量'
" N7 V3 P+ l1 _* x - );9 l" d+ `0 G2 Q) K
- INSERT INTO ta VALUES(100);; O. J6 i: K& A5 [
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件2 U/ @7 ]4 w, w. V& ? t- V, o
- */ : N0 D" A6 y; O. F4 Z
- " W$ A; t2 h, z, g& a4 y
- // 关闭错误报告
8 T8 G, x9 R: w8 j) C9 e" D" d - error_reporting(0); # {5 z0 S% }/ Y; U3 e& }9 B: w# O
3 N( w+ K) J1 O0 v$ g- $dbhost = 'localhost:3306'; // mysql服务器主机地址1 F9 q# z1 F- s: U$ Z# ~2 L
- $dbuser = 'root'; // mysql用户名
1 q: i9 e. ^ K% U7 z& u- u+ F& s8 h - $dbpass = 'root'; // mysql用户名密码7 d1 K$ i; k& o- B) W
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);5 X; d- N- k/ y: O7 W' D$ D
- if(! $conn )2 d6 l. I7 P0 r$ T5 f
- {
& O* K9 }2 J2 p - die('连接失败: ' . mysqli_error($conn));* P5 w& k2 @2 ]. L1 e
- }( f6 }; R! G8 X+ g9 [5 E* U' [
- // 设置编码,防止中文乱码
) w) T) T+ `- ?8 L( x - mysqli_query($conn , "set names utf8");
: H; v) V! J( h - mysqli_select_db( $conn, 'temp' ); 9 b: R2 d! z' Z/ `6 \) e
3 v) x |6 L9 X7 K1 k- # php中的文件锁
& R1 J% \4 ^! s$ ^ - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 # p/ X: M" ?: d5 a
- flock($fp, LOCK_EX);// 排他锁
- |1 k0 M- A0 v9 b - / u, f* V$ O6 P6 m& b8 s' F
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
) M8 x7 Y% ?$ A" U8 j v7 A( B - while($row = mysqli_fetch_assoc($retval))+ y$ e X! [. L6 p& n
- {
' i" D5 \1 C6 n: } - $id = $row['id'];
1 `5 j& F4 V6 v9 L - }9 t9 J7 u5 J) j; a4 |' T3 G
-
3 M* \/ `* W! ~! d% ?8 l - if($id > 0)
8 q1 E P6 N6 E - {
9 {8 B6 W2 _7 x - --$id; 6 q& G3 N8 V, e& j5 T) y. P4 U
- mysqli_query($conn ,'UPDATE ta SET id='.$id); # ?$ r+ i% @) w# U. }' |% z
- } 0 e6 @9 C2 B/ |# y' c* [% \+ G
- # php的文件锁,释放锁 : m: K9 v% E V& W$ d4 E& X
- flock($fp, LOCK_UN); - F4 t: m5 K/ [# _ p( }
- fclose($fp);/ o) h5 v/ [+ o
+ p. x: F' @; x- // $res = mysqli_query($conn , 'SELECT id FROM ta');
. z- ]3 {7 w, m, V - // while($row = mysqli_fetch_assoc($res))8 ~: F5 c: m9 ^
- // {
) C2 T- q1 E; m/ g2 f; j9 y - // $id = $row['id'];( {' Y+ b$ N1 r) H
- // }
$ `) ~+ ]' {/ H6 P- T+ L l - // echo $id;
复制代码
2 l5 k ?. W- ]* T' T' I" v( F/ W$ T0 |% R; u6 m
抢券活动实例: - public function envelopeSnatching(){
6 M& K6 V( U# W8 V. y( h - $lingqu = $_POST['type'];
. N: d- p! i- }; C - $uid=session('u_id');//用户id
5 ], @7 ^& I4 Q; G% c g$ r - if(!$uid){
1 a# y @8 n1 k" C) J' e - $data['msg']='您没登录,请先登录!';
( S4 @" S0 z% ^) Q7 ~- b# M, ?! Q - }else if(date('Y-m-d') != '2017-12-12'){5 H* I- L+ U3 x' o
- $data['msg']='不在活动时间内!';
" [! o0 }# _# P$ h. Y - }else{# J/ D6 Q# b: K) e2 P
- $hours=date('H');//当前小时数
0 n* a7 ]8 m* ~7 Y- ~ - if($hours > '09' || $hours > '17'){0 ^2 ~9 m+ b$ o8 ?# _( p
9 ?9 H b( i3 B- if($lingqu == 1 || $lingqu ==2){//点击10点的' ~; w# i8 C$ D t& g8 f9 n: l
- if($lingqu == 1){2 ^4 K. |- }; n2 J+ V6 A; U! @
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
1 J Z1 X. R' K2 {, n - if($hours > '09'){
n4 m/ ^ L7 G. j: o4 b1 m& Y - $num=mt_rand(25,28);//优惠券金额
5 T7 u; w) L& b/ s0 A' X% q+ b - $id=1;6 ~( l3 `4 N- l& D( `
- }
$ b7 Y) }3 J0 y# k+ J3 K! S - }else if($lingqu == 2){: W' M% R5 O2 C; D7 j5 { J
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();7 _0 e9 n% N1 L) h w
- if($hours > '17'){
( r4 X! u+ U- N9 Z# k' g5 c - $num=mt_rand(50,55);//优惠券金额
) f: H3 G. L3 v9 Z9 u - $id=2;
7 @2 J! u7 a. \& W - }
- z6 z* M4 Z1 b( ]/ ?4 t, M - }
, L. o9 x3 b: t' m7 l - if(!$id){
$ ~1 V0 @( z" }+ @$ d - $data['msg']='时间还没到,晚点再来吧。';4 u' J- a% q j h) X
- }else{
8 u; i- s" K5 o) L4 l& G$ h - if($is_lingqu){7 E, X6 Y) g" X8 U
- $data['msg']='你已经领取过了,留点给别人吧!';. M' L9 V6 I2 T; `0 u% ?
- }else{1 f% D; ?$ ^$ l. |
- //锁表) u! |# I1 |. h X4 N. }1 l& v
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');8 V; ?: W7 a! F3 s
- $active=M('active_num')->where(array('id'=>$id))->find();
- M+ u% M0 u3 z% t3 j) ?7 a - if($active > 0){/ ^: c2 P7 L/ n9 n
- //开启事务2 A3 n, f$ e7 X# @. N h
- M()->execute('start transaction');
4 v2 H' ]3 K5 k" [/ T - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));1 l+ y7 ^- A8 k7 R. t
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));. @: g1 X0 _! c4 }" T, z
- $members_preferential = M('members_preferential');" V7 L3 B9 h/ A/ k+ W
- //对应投资金额,7 R4 {- q- S: a# f0 P/ N
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));9 k! W4 W' |& H0 k
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
, N X8 ~3 i+ K' S& q& H; _# k - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间) A! r7 _8 X* ~
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
( _- R# j4 [7 o8 b' b - if($save && $add && $add2){1 R. k: C4 k- y1 ]. h6 x' g: ?4 ~- l
- //事务提交+ v( I7 t5 o3 c6 B: ]
- M()->execute('commit');6 v8 I; ?- T2 h/ F
- $data['msg']='恭喜你领取了'.$num.'元优惠券'; V+ z c4 J0 o* F) m3 w* o
- }else{# n3 Q" P% Z1 B8 ? X
- //回滚8 [% v7 b+ P. f
- M()->execute('rollback');
% g3 s3 n0 \$ P - $data['msg']='未知错误!';
O; F+ J4 S) m. {. Z - }5 b; p& h3 q0 n2 S
- }else{0 X; o! t% w9 H- i7 y3 r6 g; ~8 T
- $data['msg']='红包已领完,你来晚了!';
5 A$ ?. F4 n' ~6 B- d: U - }+ F0 ], G4 \3 E7 J
- M()->execute('UNLOCK TABLES');
# U% |5 F1 }. c. h; V- H - }! l8 J& N% `' D* k9 R
- }
( S/ l! j" H# q r2 U5 F - }else{
2 {: J# F9 Y. R - $data['msg']='非法操作!';5 {( J+ p9 Q5 U
- }# ~7 q6 W2 E, r. K
- }else{
W5 k, s1 n8 r) U. v% w - $data['msg']='还没有到活动时间,请晚点再来哟!!';; x( i0 ?& Z& H( ?: c5 m
- }8 M4 J2 q" f8 r) o1 f; z3 C
- }
1 \6 e/ T0 A( J, @) x8 N - exit(json_encode($data));
3 [8 [9 ^' [* Y5 ]6 L' T - }
复制代码 - b/ _, f1 g: S/ c
# h% K$ h! h2 Z H2 z
|