模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
* @( K4 P: O% Z# uMysql中的锁语法:" F2 e$ ]6 x {; n- V
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
* G% }3 ]9 X9 QUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
0 V5 M. y6 ^. |' x% h9 kWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞0 T3 `6 Q6 d1 b% X' C
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
% h8 P7 ?8 ]) w文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
- Q( t4 X, t9 A1 d( r+ [+ W测试时,有个文件就行,叫什么名无所谓 总结:6 c* K# }1 m3 x% h. t
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:7 I7 ?; f, p7 w! O0 y4 l
1. 高并发下单时,减库存量时要加锁
, R. k, l. |$ V! }3 A+ r# E2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*: P; m4 b2 l" o `5 V$ {$ s$ F
- 模拟秒杀活动-- 商品100件
" R" \2 {, V. |3 ]" J" C - CREATE TABLE ta
; [7 d+ S$ P+ x$ r- W1 U5 A - (
' k4 {& d0 l9 X - id int comment '模拟100件活动商品的数量'2 z+ F: c8 a- B* r# S" X
- );
/ ?! P; j" I- i# ` - INSERT INTO ta VALUES(100);
" r5 u& ^: F8 b/ @ f$ l) s; i5 b - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件( {1 L5 E- v. S9 e/ j. D
- */ & V5 R/ d8 J: Z( j) d, D0 B
- 7 m( l% }1 g+ W& @6 \& \
- // 关闭错误报告+ ~0 G! u3 b7 o* C" S0 k
- error_reporting(0);
# b: Y4 K5 y$ A8 H) D - 3 Q# G) T3 ^& d& h( X# \; D
- $dbhost = 'localhost:3306'; // mysql服务器主机地址* n7 F) t/ o4 S; I* d- T
- $dbuser = 'root'; // mysql用户名7 D7 r" X9 H/ p
- $dbpass = 'root'; // mysql用户名密码
8 e- o* G3 ^8 ? O& l; A) U - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);; J; o) B& C7 h0 i3 ?. F2 x5 v
- if(! $conn )
) Z, o) j. Q- e - {. S7 M4 W$ D* p0 v
- die('连接失败: ' . mysqli_error($conn));
" W# L; S9 s1 x0 h7 M; r) u - }. I' y& I( h S% T+ G2 S5 [, j
- // 设置编码,防止中文乱码+ `% F) m! ~) S; j: I
- mysqli_query($conn , "set names utf8");- u- D. l( q9 [. Y9 p
- mysqli_select_db( $conn, 'temp' );
2 U. j8 |) V4 N5 b; m' `" [% {/ x
3 _! G) u7 Q n) u' Z& ^- # mysql 锁 " C, u" M! e. N
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
3 h, q$ Q: D' U+ e+ ?) f - $rs = mysqli_query($conn , 'SELECT id FROM a'); 2 _% t0 d7 V( L
- $id = mysqli_result($rs, 0, 0);
& K" W9 _7 {) l9 m. s# f, | - if($id > 0) 3 b( s* B" g/ p6 Q5 X" T+ Z8 ~+ O4 ^
- { ! v2 Q6 d; T e2 p$ H% t
- --$id;
. ?' B- L5 ^* z2 t1 C/ b - mysqli_query($conn , 'UPDATE a SET id='.$id); / v# L$ m7 G: D. D# r/ K
- }
; @, {9 s5 B+ @. x
+ d P" y1 T8 v* A' \* J1 e6 k( f2 X- # mysql 解锁 " P2 U0 w+ \; w/ k1 P1 b+ ~+ Y
- mysqli_query($conn , 'UNLOCK TABLES');
. P9 ~8 S% z- C5 ?: ~, G8 `# s9 N: B - //查询解锁后的id值/ J! o, S* s9 g/ Q
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
! g# f: ^' t1 ?( U- J# J1 Z - // while($row = mysqli_fetch_assoc($res))% D7 f" F" |0 ?1 [
- // {
$ r$ V$ ^( v8 o( |, _$ F9 E - // $id = $row['id'];4 z, H+ U# m1 O. M2 o; O
- // }
( b& v4 G* {5 s - // echo $id;
复制代码
. [( _- p. Z2 B S6 M( e( l( Y3 s5 w; v3 i9 B" N2 i, q
C) Q X# Z0 L% z0 U
PHP文件锁示例: - /*
; o# H1 w3 j" \, A- h - 模拟秒杀活动-- 商品100件
! I0 H( o) b& e. ?/ m3 a - CREATE TABLE ta+ \: L6 T9 y' d* G% r3 @( l }2 i* d
- (5 m+ K4 w( _5 w. G4 G1 M
- id int comment '模拟100件活动商品的数量'
; M; R* U& m* ^7 _- O - );
$ u4 r) [$ A- O - INSERT INTO ta VALUES(100); t" X. H7 d1 {& \3 R9 J8 G
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
1 J) b$ j9 c* [! A" f - */
2 r2 i, n/ \) V6 _8 J$ Z -
8 V* y/ }% ?& ~: v( z9 n0 W2 O - // 关闭错误报告; Y% N& g7 }' N4 D
- error_reporting(0); ' W# {4 f+ ~8 Z7 J r
: k9 _0 v# R4 Y" g0 h- $dbhost = 'localhost:3306'; // mysql服务器主机地址! `+ u# T6 P5 w& M2 Y
- $dbuser = 'root'; // mysql用户名
' d0 G8 d% D, q7 r - $dbpass = 'root'; // mysql用户名密码
$ J* c" W P* s% b - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);2 @2 K* q9 d% ~& V
- if(! $conn )
8 c* l$ f6 ~' s% B - {
: H1 F, k; k& t! C* o7 Q - die('连接失败: ' . mysqli_error($conn));
# Z* A1 w" R1 R+ x. v9 J - }+ r. ?" F) T) C6 x8 q( v8 k
- // 设置编码,防止中文乱码7 J/ S7 S- H) c" C, t3 _: Q9 H
- mysqli_query($conn , "set names utf8");
. ]: `3 c' b) ^; b% R - mysqli_select_db( $conn, 'temp' );
5 S W1 `$ e2 ]7 e q
/ ]4 R1 m6 ?0 X9 m4 j, J- # php中的文件锁 / s3 g1 j1 F2 [
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
) k0 _) E' p0 Z - flock($fp, LOCK_EX);// 排他锁
# V W, k, M" d4 l* f5 { - 3 r- T% J1 Q2 i7 S0 v/ A6 w0 J
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); # Q0 e4 x0 S( I8 ~
- while($row = mysqli_fetch_assoc($retval))
, ~1 W, G: e% P2 L" |; j B% S0 \ - {( I- T; L) I8 P# l9 c7 M; k; n
- $id = $row['id'];% M/ V$ D3 V7 ]0 i) C! N9 s5 m
- }
' r( I7 _# B. {& {0 k8 O3 z7 t -
( h! }8 m6 ^7 ^ - if($id > 0)
- d8 j! C1 o, k+ d2 `+ N - {
$ x7 [( p) T, H2 Q7 g+ z9 ~ - --$id; ; s' e# g! X+ z# w) j
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
& D) N2 Q) x( h5 u - } 4 s8 \! p9 w6 X+ K
- # php的文件锁,释放锁
' L4 A# @4 l: E5 G9 W - flock($fp, LOCK_UN);
/ C# v. z- i2 R9 _( X7 U - fclose($fp);
/ ?" u* m6 R* C. p# Y6 k* ^+ V6 ]
6 K7 }* P! C5 A8 F/ A) c: C7 |; G- // $res = mysqli_query($conn , 'SELECT id FROM ta');+ k- S4 O8 y, X1 I. j- W) K8 a% z
- // while($row = mysqli_fetch_assoc($res))7 D! ^2 Y6 s: S+ v1 W: P6 Q* X
- // {
& T$ l8 K! l8 a$ h* H% I6 h& [/ X' r - // $id = $row['id'];
/ G" y! e0 w0 F, A. v - // }
6 l( @8 R4 t. l, S$ B0 d- E) Z. e - // echo $id;
复制代码
2 v+ i7 F' W) r$ x* j: \: i& a% y; L, B1 C9 R. }/ V+ M
抢券活动实例: - public function envelopeSnatching(){
" t- @* G' F& G6 q0 @0 z; Z - $lingqu = $_POST['type'];
& `' k& B8 C0 \8 N - $uid=session('u_id');//用户id
* I m3 X" z! ?" d) u# [ - if(!$uid){
0 U8 k0 T: ]$ ]" S - $data['msg']='您没登录,请先登录!';
6 B# I U+ K+ c4 r3 \/ ? - }else if(date('Y-m-d') != '2017-12-12'){, p( {( V9 N7 ?7 a# q. ^- p
- $data['msg']='不在活动时间内!';% ^* r, B3 Q! _! y
- }else{1 z* i& J; N. v7 k v9 X( S: o
- $hours=date('H');//当前小时数
" |" Q$ X. R. C - if($hours > '09' || $hours > '17'){* X% b, U, [1 q3 V2 y. M. s
% h S' z5 F; K9 ~5 E- if($lingqu == 1 || $lingqu ==2){//点击10点的% v. \4 ?! A K& K/ c A" s
- if($lingqu == 1){
$ @' W. s) Y: Z. Y2 d - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过+ b4 z6 G3 J7 I+ E1 Y! T. [+ s
- if($hours > '09'){
# h$ B. t2 r: i N% K - $num=mt_rand(25,28);//优惠券金额
! R0 H6 T' @- Y# X$ m - $id=1;( x" x, K" }9 t4 q" A" K
- }
8 I- W: f1 Q* m1 G - }else if($lingqu == 2){$ W5 o3 S9 `0 l I. P: @& `7 K8 W) w
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
7 w* p7 g) b' z" d - if($hours > '17'){8 T: O* D8 L( `
- $num=mt_rand(50,55);//优惠券金额4 G- z4 X5 i3 Y X7 x5 q! E; }# \
- $id=2;
1 }" _4 j( o* C* j - }8 ^0 @7 Y3 ` a5 e6 P, k
- }
7 P) O% _1 l/ f! P- T% {- t - if(!$id){
2 w1 I; }2 Q5 m! A1 U# Q - $data['msg']='时间还没到,晚点再来吧。';4 ^. A3 t4 R5 C! J" ^! ?' T& y
- }else{
% K( M: k) l& @7 f9 K3 R7 l - if($is_lingqu){- H7 h' Q+ P/ t' R* v9 x- W
- $data['msg']='你已经领取过了,留点给别人吧!';0 b1 b+ |$ a' [% S C5 T. P
- }else{* t. Z8 ^0 ~4 V% g
- //锁表& c0 M! i) x( y2 L" I. E
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');+ J0 o- p+ f' i" w4 e
- $active=M('active_num')->where(array('id'=>$id))->find();' y: G- y1 s2 C
- if($active > 0){0 E' P* x! R0 S$ ~
- //开启事务( v2 q5 l6 n# \4 h5 m: m
- M()->execute('start transaction');
4 c A6 ~8 R! a$ T% C/ b) p$ l1 C8 J% E - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));- r3 W1 {3 J* m& I" l* z- v
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
! @. A$ K; X- ^' b4 t" z - $members_preferential = M('members_preferential');$ k: z- J9 q0 \% Y: @7 r. H1 l
- //对应投资金额," x* G% b4 q8 h& j( ^. _4 r
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
+ V9 |1 |7 Z: ` r - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');* o- v2 L: q$ G# c
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间( B! F; [0 M- Y& h, V2 w5 ?9 g- {
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
& G. Y/ v7 t% g - if($save && $add && $add2){6 p8 y/ L2 y7 T" X
- //事务提交
3 A v* T3 b6 \0 L o - M()->execute('commit');+ J4 t7 S2 F- w! \2 G
- $data['msg']='恭喜你领取了'.$num.'元优惠券';# b) a$ h8 K! F) S
- }else{2 m# {5 d$ g* k" J* K* q
- //回滚8 f1 w+ t$ p F/ {
- M()->execute('rollback');
) C7 n7 `& u( |! g9 { a* { - $data['msg']='未知错误!';
8 x% N/ W4 X" ~$ u - }
1 L0 _8 k, t$ L6 j @9 w - }else{
$ N& `( Q( y- r3 l0 J3 ]0 U% d1 x - $data['msg']='红包已领完,你来晚了!';
1 N0 V/ d, @8 t. {: M3 e1 | - }, W/ L5 E* v5 ^) _! S% O% [ L
- M()->execute('UNLOCK TABLES');% _# [3 P& z) R7 l1 z
- }$ T% e$ D) j$ X+ U) V% ?
- }1 b r+ E- A" {- T& A! M2 ~$ m
- }else{
/ J( @ w1 t5 {6 S5 A - $data['msg']='非法操作!';* A4 B% w/ R# o K; ^
- }
6 \9 E5 _8 t# a. z9 t - }else{& |/ _6 x$ C# [; A) `
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
E% w8 z( V' H2 I* u - }
% L9 N, j6 r* L5 J1 \$ y) |2 p; S - }7 e" V, E9 D. o6 m( e4 p" I+ I
- exit(json_encode($data));
4 C- F1 ]& U4 C - }
复制代码 1 F; @( B) {' M) j9 U+ o1 U* i
4 {' {; R, E3 G- z |