模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 , D: e7 Q* s1 J
Mysql中的锁语法:
: G) P7 Q! C1 q4 p# f' w" GLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】1 Y5 y" Q+ i' Z* Q5 O) h3 j- m
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表: ~- U6 Q: C3 A& K- I
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞1 r% d1 G" F) v. [
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :$ N; C6 m# [0 a$ t' s; q0 u
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
2 O0 F& M# H* ~6 n& Y" ~测试时,有个文件就行,叫什么名无所谓 总结:
+ S8 M6 }) a5 a3 h1 x) {项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
) p* e7 n- N& I' U0 ^1. 高并发下单时,减库存量时要加锁
) x& y l5 l7 d. g; h" W9 h2 y2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
q( M2 v4 `9 T4 B" U - 模拟秒杀活动-- 商品100件5 F2 G! A1 f0 J7 D
- CREATE TABLE ta# a! L/ \! O2 b7 Q7 L/ u9 @
- (
8 S8 i5 l O- }3 x: C& N - id int comment '模拟100件活动商品的数量'. M. {/ ~, ?7 J9 Z6 w
- );8 N: o& g2 i+ }" Z2 ~
- INSERT INTO ta VALUES(100);8 c/ }' k! { ?+ ^
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件# I; [% \' L, f! m$ Z( X8 }' v! a
- */
- d5 u# t! ?) K5 t; N+ n3 ` -
5 ]( x, @% l, @! Q! O - // 关闭错误报告3 M8 ]7 m9 o2 \3 _/ W9 P! e+ \8 b; ^
- error_reporting(0); # H; C2 r6 ?* A. }& Z2 g/ v5 G
4 N1 p2 S" b! J! r- $dbhost = 'localhost:3306'; // mysql服务器主机地址( ^) ^+ q( X! d: k* Z- a' E8 j
- $dbuser = 'root'; // mysql用户名# t1 N9 b- `/ a# U! o
- $dbpass = 'root'; // mysql用户名密码5 y6 T( p6 M7 I6 F
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);3 l( I" ^' ?- X% j+ J3 x0 h
- if(! $conn )
& b) h0 l2 D( S0 P: z( H2 P# ? - {
% b2 ~1 m) @! w2 K' l& E, D: Q+ u - die('连接失败: ' . mysqli_error($conn));( b! f1 P9 x" F# V; F% _4 P6 W
- }
, Z2 x; }$ |7 e! G* p0 ? - // 设置编码,防止中文乱码* m% G. _. g- L, L. l* i
- mysqli_query($conn , "set names utf8");- Q3 z1 q# l3 T S8 B' a
- mysqli_select_db( $conn, 'temp' );
* F$ {3 z, `( A& @" |# ~
0 O+ v( t: N7 H& X! M* I' p c+ }+ N* G1 E- # mysql 锁
0 _* d# Q; [/ R& V6 P- g, G I+ F - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 2 H+ Z1 u* Z0 [4 v
- $rs = mysqli_query($conn , 'SELECT id FROM a'); ) R" z+ I2 N) H% o* C; k
- $id = mysqli_result($rs, 0, 0); " w3 Y; u0 `0 J$ @
- if($id > 0) ) V( P2 W+ t+ t2 b; d# B+ U; F
- {
+ f: l: m0 I: U/ ~, k - --$id; , }0 `- Y8 o P* C F5 |9 Q# L
- mysqli_query($conn , 'UPDATE a SET id='.$id);
& `6 a' E) j# H% V - } ; |2 P4 u( c" r' u* d
$ t4 h- c# d6 \$ F- # mysql 解锁 6 I9 @8 U' {5 A/ M: X$ S) l
- mysqli_query($conn , 'UNLOCK TABLES');: q; d# Y; Y0 X6 C
- //查询解锁后的id值
2 z) z& [6 B6 F6 G - // $res = mysqli_query($conn , 'SELECT id FROM ta');6 c& q Y% S3 k/ A$ f. s6 P
- // while($row = mysqli_fetch_assoc($res))
1 `( M5 X2 w2 P - // {0 P' `1 @- R, ^& F
- // $id = $row['id'];
& \) V" t% ]4 d - // }. y' M; `7 @# c
- // echo $id;
复制代码 Y0 ]& p) j/ ?: {2 Y3 O
# e) X* w) ?+ c5 }" }- [" N$ x
& n0 E ]7 D5 y# j. S1 ePHP文件锁示例: - /*
* r0 N' |+ v. A$ Z- ^ - 模拟秒杀活动-- 商品100件
( z4 \+ k) P8 i l7 B8 V. A - CREATE TABLE ta
' z- [( i, G8 P& g7 n5 i. l& }" { - (; X4 l( H& F1 T. `2 p
- id int comment '模拟100件活动商品的数量'
; h) w% ^' @: x& ^5 V2 | - );3 P3 q6 ~+ W3 m+ K. L
- INSERT INTO ta VALUES(100);9 M3 f5 G5 X' q0 K9 R
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
" U( Z& E6 k7 m& b - */ 0 W8 b3 b5 Y- ?; `( I; y$ t8 R
-
7 \8 t z& I& X% e( `& e- @ - // 关闭错误报告
( B3 g, _6 ?8 x6 C4 Z5 ~ - error_reporting(0); - ~3 q& F; C& c3 a. c& q5 p! X! Z
- 3 J3 L% \% A" y j- w+ D' Y
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
: v8 U: W) D2 g/ P$ L* G+ X# e1 ? - $dbuser = 'root'; // mysql用户名
% W" v& Y0 J) p, }( A. S - $dbpass = 'root'; // mysql用户名密码4 M6 }8 J8 u( i% s
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);" m7 }) h. N- M* O6 v
- if(! $conn )
6 A7 D, j* T! H, I! o Z+ e( c - {
K, u' ~; U( L5 J, r - die('连接失败: ' . mysqli_error($conn));/ ?( D7 n. d- q2 ]
- }6 W) o! \4 c" z) G; x) X; {' o8 O
- // 设置编码,防止中文乱码
/ R: _7 [" [- i5 I - mysqli_query($conn , "set names utf8");7 ?; y$ q# L5 J1 f4 w0 U# m
- mysqli_select_db( $conn, 'temp' ); ) P9 m2 ~' q/ m/ g6 D
- 6 |. U8 ^) {4 ^; p3 s
- # php中的文件锁
: J- ^' C) P: f% h4 K6 j - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 $ r- w3 U# @; X' |$ f6 w
- flock($fp, LOCK_EX);// 排他锁
) _- J% \# Q. p3 U+ B# V/ S/ I
7 {9 f+ t1 R+ ~5 m! b- $retval = mysqli_query($conn ,'SELECT id FROM ta');
3 X T/ ^. Y% U$ p - while($row = mysqli_fetch_assoc($retval))
5 s/ ~; `. c/ L( R5 [5 `. t- I - {! E; h# H5 Z# t `* D3 L) j
- $id = $row['id'];
9 }+ {7 c3 j7 Z9 X# H - }
L# |( _3 x+ ^* V% Z. M4 \0 ] -
# g: ?9 P3 f6 f/ o - if($id > 0)
r8 P# |8 m1 g - { 1 B' Y; c# w7 `1 V ~
- --$id;
2 U2 r+ F1 M$ L8 F: N3 A" |$ V' x - mysqli_query($conn ,'UPDATE ta SET id='.$id); # t" s. U6 p$ V' u- H P" S
- }
3 o# u" g1 c& s2 ]' C - # php的文件锁,释放锁
' O* l# L2 C V0 k* ]2 M8 M - flock($fp, LOCK_UN); ; {+ ]4 }, n' u& v
- fclose($fp);
& \. ^+ V6 b- z0 N6 V
- q4 t1 j1 S- G [ @3 ~! w9 G- // $res = mysqli_query($conn , 'SELECT id FROM ta');1 x3 A3 B% C% L5 E
- // while($row = mysqli_fetch_assoc($res)): l& `" W& _) C, j& h: Z5 p6 B
- // {5 v! g. q/ a( \; D# `
- // $id = $row['id'];" p0 s1 E F2 [) f9 O
- // }; r. E4 j+ G# g+ }3 _ I
- // echo $id;
复制代码 4 z% p5 t; C8 r- i' J4 s
* T2 _% s$ p n5 {+ f% v
抢券活动实例: - public function envelopeSnatching(){
! X/ K9 W& I2 [, n" k - $lingqu = $_POST['type'];2 M7 H. \# e( d# a" T
- $uid=session('u_id');//用户id1 z' P8 j5 \5 q: r) G
- if(!$uid){
+ K; ]; {: v5 N* I# Q - $data['msg']='您没登录,请先登录!';
0 C* w6 g& g9 M3 i) _$ |1 [ - }else if(date('Y-m-d') != '2017-12-12'){8 X. g5 E( x- j$ L! e0 O
- $data['msg']='不在活动时间内!';
% w0 E9 Z! P; W, S# F4 ~ - }else{7 f% t( z6 t: r" h7 `0 B
- $hours=date('H');//当前小时数) O4 \% {$ C, S7 g1 d l+ H- }
- if($hours > '09' || $hours > '17'){
5 U/ `1 P# D) J2 R, v
+ M$ k7 E4 x4 H: n. ^- if($lingqu == 1 || $lingqu ==2){//点击10点的- T' L1 p. m0 S( {% \& f
- if($lingqu == 1){6 T: N5 j7 ]. E# d1 w
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
2 x+ N1 k/ M# e8 q9 z" X8 W - if($hours > '09'){
2 M/ g. ~ Z3 g, T3 v - $num=mt_rand(25,28);//优惠券金额
' J8 l: B" s$ v+ j6 [ - $id=1;
, b! |: G1 p3 u. _5 l7 P" T7 H - }
, m0 t% {- E7 u) U) P0 Q3 T - }else if($lingqu == 2){
7 p; ]9 K/ [" W - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
- j% r8 T2 s7 D" M$ q - if($hours > '17'){
8 e, A2 @; E# k- ? - $num=mt_rand(50,55);//优惠券金额" P# G9 E( W) ^
- $id=2;' @0 m5 D4 M7 _8 \5 |2 J* w& v
- }
+ P" z( {4 j3 i( U9 ~7 c - }
5 v7 V. ?9 D; ]& S - if(!$id){
3 i p6 M& d' w9 C0 G- ], t! U - $data['msg']='时间还没到,晚点再来吧。';' p' m: D( g5 Z. _# E% `
- }else{
' D4 k1 t( ? J- t1 i - if($is_lingqu){
: V7 D, d1 d( B- \: Z& { { - $data['msg']='你已经领取过了,留点给别人吧!';
& b: p/ {! } }" j4 N9 s - }else{% i7 X' `0 f5 c+ d( L7 f6 s
- //锁表
) `9 T) }9 f4 Z1 ] - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');4 Z @3 L1 S2 u; v
- $active=M('active_num')->where(array('id'=>$id))->find();
7 G& A4 E; o0 e2 _ - if($active > 0){2 r. k; {" q: y1 T# j6 o( _
- //开启事务
# q/ e, R( T0 n& M# b6 u2 R) T - M()->execute('start transaction');
& ~% C& e* o3 X2 b/ J - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
1 p: Q7 i+ U$ C9 `! l3 s - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
9 `* B6 }8 Y$ k" k& q - $members_preferential = M('members_preferential');0 q+ p0 g0 w/ N) a* d H% s
- //对应投资金额,5 s( \. _, P: ]8 r5 {
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
/ j0 I7 \1 b+ G# \: X2 U - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');; _. F" H+ e! U) L8 t5 |
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间, m) }5 B; `5 q# e. m' d9 O- u3 N
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
1 S+ t, N1 ]- R$ ^( r - if($save && $add && $add2){0 c% B3 p9 u6 A9 n9 [! `
- //事务提交1 g# y& ~1 j( I( w0 B. v
- M()->execute('commit');
' b6 _$ f3 W0 x* N2 C6 f - $data['msg']='恭喜你领取了'.$num.'元优惠券';7 w" U; J$ N# s0 s7 X: A# A6 U
- }else{0 t9 I. z' u( r) v& n# v' ^' h
- //回滚1 u w9 M V4 U
- M()->execute('rollback');
3 \# w$ d7 ^: ]% A" S - $data['msg']='未知错误!';
& B: \2 R) |: s; h9 H8 z - }
- p6 R3 N4 O, [9 m9 r2 C+ O - }else{
f8 B% i+ F6 g - $data['msg']='红包已领完,你来晚了!';
% e" M. O3 n% I! V5 P, y; `, X - }
& a+ |) o2 X& n( z - M()->execute('UNLOCK TABLES');9 Y0 K; ~# B u8 O) N
- }& R2 u; e8 M0 I) n+ X7 m2 E
- }
" M+ [1 y3 p+ Z. B - }else{$ \4 G8 k u9 f; M, Q; k4 o1 c$ E
- $data['msg']='非法操作!';, K3 N% E9 z& c5 z! c! p- p/ M" G
- }
1 W2 h- N/ d' C5 f2 o! }# G - }else{
( ?' }, Y& q$ H; N - $data['msg']='还没有到活动时间,请晚点再来哟!!';) {0 p4 ~, U+ g Z; {% d
- }
+ g4 o# h) }2 c: N' D- Q* K- ` - }' e5 a# v. `% ^) N" u A, e( I
- exit(json_encode($data));
7 X: i7 b% C0 R% E& l+ L - }
复制代码
E1 y. U' }1 Y1 u
" E3 L B; S+ v- i |