|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
9 `% V9 o8 m% M2 o5 F2 T8 j UMysql中的锁语法:
4 ?4 W) _. N5 J, r; }LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
/ ]0 |% V) c' Y2 I/ rUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
6 ~$ k" R$ N8 q, eWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
2 F6 G/ |& q2 ?% e注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :) f, `; o: u: N
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。. t: k* A, X+ W5 P/ l% P, u
测试时,有个文件就行,叫什么名无所谓 总结:
3 i+ ~ X3 h g; e- D项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:7 j* [% m+ I% O9 N, m" u2 k/ f7 g
1. 高并发下单时,减库存量时要加锁
$ m2 [% G- R$ g- C2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*' M, L& n; p' b- q" D2 X6 o& h. b/ w
- 模拟秒杀活动-- 商品100件
9 D E/ f' ?( O - CREATE TABLE ta S- c: Z5 C+ J4 R% b" T" v4 G3 _
- (
# B( w% {4 c+ W5 s - id int comment '模拟100件活动商品的数量'
$ ^ l, ] D! A3 y. l - );' l: S! o8 h; U5 V. u7 k- b
- INSERT INTO ta VALUES(100);
0 e1 _! e! b% W: o6 o! p - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
& d3 H3 h* [: T; l* N3 `5 y - */ 1 J+ U8 e' f5 s ]/ }+ @5 j
- 0 |. q: v4 ?( N
- // 关闭错误报告 @0 g2 n2 c8 z' @
- error_reporting(0);
# m1 C, v; l, O5 M - + L' ?! G# P5 h9 t
- $dbhost = 'localhost:3306'; // mysql服务器主机地址' L, o( W0 e: \
- $dbuser = 'root'; // mysql用户名
3 A; f; q; Y4 U5 I1 u+ b2 q2 N' l - $dbpass = 'root'; // mysql用户名密码
, d% p3 ]. ~9 p/ u7 A3 H& x) | - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);* D" M* b+ b2 l
- if(! $conn )
% U1 {$ ?; w- x+ E* L4 `9 L - {# [# Z* e" l: {2 J7 D6 o9 V
- die('连接失败: ' . mysqli_error($conn));
9 p* k! F, e* j. Z8 K& Q2 G - }
4 a" q+ _ g& p. [9 [2 f - // 设置编码,防止中文乱码! q+ n3 k2 a) A3 D5 S0 q1 d( ]
- mysqli_query($conn , "set names utf8");. f' J1 `$ D5 F+ G$ T+ P
- mysqli_select_db( $conn, 'temp' );
3 \: @- [8 R- L- \- o1 ~ { - " u3 H R1 Y4 k4 q7 Q
- # mysql 锁 $ F* s3 `4 Y) f/ h B
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
6 F0 A$ p; s3 Q8 y7 @/ U y: E - $rs = mysqli_query($conn , 'SELECT id FROM a'); 7 N' b* m6 L7 X6 g3 b
- $id = mysqli_result($rs, 0, 0);
0 Q" K0 D( f" D - if($id > 0)
- P* b. u/ a& z4 [ - {
2 d& a: ^7 j+ v* u - --$id; & A- D4 p7 ?3 b& a8 S; _- q
- mysqli_query($conn , 'UPDATE a SET id='.$id);
$ c2 Z' j( e8 L, ~5 p - }
+ w0 s* a$ F. _+ H- `* U/ | - 5 Z8 I8 b- p& r2 |/ c, c: c1 V
- # mysql 解锁
# ^6 W2 h1 K+ F - mysqli_query($conn , 'UNLOCK TABLES');
' h5 D- }/ B# w- O8 O. f: y - //查询解锁后的id值8 t% M. B; t4 K: T' u
- // $res = mysqli_query($conn , 'SELECT id FROM ta');9 I% b! K5 G" \' e H
- // while($row = mysqli_fetch_assoc($res))
) U8 k6 [0 [% e. [* y2 L; r - // {+ u9 D, K* n* [4 ~9 a" U/ d
- // $id = $row['id'];
5 b) u8 }5 r$ M2 w2 j - // }
- x0 P" K/ c0 I" M0 x+ x; B) f - // echo $id;
复制代码 9 Y9 q" R& k; h0 q
9 r0 n% n Q2 j$ k
1 n% |5 n, H8 g# b
PHP文件锁示例: - /*
' z: L. p) t0 X: c1 b8 U - 模拟秒杀活动-- 商品100件, M' r) H) ^) ]
- CREATE TABLE ta+ t8 l5 j* d) O& r: j$ ^; U# t
- (, A# X( U( C# o
- id int comment '模拟100件活动商品的数量'
; n7 w( {! W9 J9 _: f" S - );) X# t0 I, G( ^- J" @. ]* G) ?
- INSERT INTO ta VALUES(100);) x' f5 m6 O, i D" i$ u2 G
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
9 o) T; Y; Y/ u% w; ~$ B - */
5 x1 g4 X( Z9 y& w5 e, K -
* s; [- k9 c7 Y; {5 Z0 b - // 关闭错误报告
9 C! V4 k) D* M2 }* w" P: U7 \) V - error_reporting(0);
% _6 l( Q& F2 _! a+ M) r - * ]/ J1 ~ t% R9 ]
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
$ V& \$ T5 [1 _( D3 c; \5 D - $dbuser = 'root'; // mysql用户名
, r% b1 B, r0 w9 r - $dbpass = 'root'; // mysql用户名密码6 ^1 [& Y5 p. W6 i( D
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);$ n" s: l6 W5 }0 k, Z% f# O: x
- if(! $conn )
% F; w3 e' ^$ q7 _( i; s7 A! ^- ]( o - {
; X7 {3 D5 o: W4 V5 G$ T6 E - die('连接失败: ' . mysqli_error($conn));7 k' m& _: `* _! r
- }
) y, ~9 e- b0 R7 i1 f, } - // 设置编码,防止中文乱码
* z2 N0 T! Q/ P9 t# ` - mysqli_query($conn , "set names utf8");" [- x& q6 S! ?% W6 `+ Q
- mysqli_select_db( $conn, 'temp' );
* \, R) ?( X0 J2 c% p( o - * C& p0 G. @5 H; n! S+ A
- # php中的文件锁
3 `- a. H# H) _ - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
2 O+ v* e/ z; W& Y - flock($fp, LOCK_EX);// 排他锁 ( K/ J2 m/ t9 b# ]9 h* ?6 l/ X( \
/ n |2 q* ]; E" r; _& Z: E+ c- $retval = mysqli_query($conn ,'SELECT id FROM ta');
3 k# A: U( c5 y - while($row = mysqli_fetch_assoc($retval))! n3 n' \3 z E4 o" T5 ^( O
- {3 h2 h& A; `1 e. t
- $id = $row['id'];# V4 _$ }8 z( U; n& Y$ Q
- }
$ e/ F5 ^. L$ t1 I - 5 k$ m6 L6 E! |: \" m3 K1 @
- if($id > 0)
. I2 Y0 M/ k2 @4 v - { ( ]2 }! Y& c0 Q% b8 n
- --$id;
& Q! P& U I- S$ E' C { - mysqli_query($conn ,'UPDATE ta SET id='.$id); . l' e' h, T- ?6 T
- } ; O9 L) n, k k& a: t' d
- # php的文件锁,释放锁 5 r0 g/ P7 f! d
- flock($fp, LOCK_UN); 0 k+ e1 t( P9 ^5 @6 j. F8 v
- fclose($fp);
: J' e+ v5 c8 }$ P6 x/ _) e4 s0 F - " O0 y5 H! ?2 i( N( l3 j" d
- // $res = mysqli_query($conn , 'SELECT id FROM ta');$ b" Q0 h7 f% Y. W n B
- // while($row = mysqli_fetch_assoc($res))
6 ~* l- w2 O ?2 n$ a3 q - // {
3 f6 ~3 Z. R$ E - // $id = $row['id'];
; g( F" F- u- F2 g3 _ - // }# N. g' R/ ^( \( ]9 N. G j
- // echo $id;
复制代码 o* x5 [8 q) K& R8 _" E! M
' L0 o2 Q) a; E9 m8 y% c$ W& R
抢券活动实例: - public function envelopeSnatching(){) u% e, s$ M6 F5 Q% c* D6 b
- $lingqu = $_POST['type'];8 ^+ q F$ P- F; S; X7 ]8 G& l
- $uid=session('u_id');//用户id
& Z& @& [7 Y: H: D2 A! g( r- w - if(!$uid){
: d/ d' V8 v# v2 g# N0 ~) L& d L - $data['msg']='您没登录,请先登录!';! m% \( d. h' G2 ?3 G
- }else if(date('Y-m-d') != '2017-12-12'){
$ w# Y2 Q- \( u* Y! N0 I: v - $data['msg']='不在活动时间内!';
$ D3 ^- X0 ]" J, } B" N - }else{
1 u0 t! B- o- H - $hours=date('H');//当前小时数, T: X+ j4 K4 G: a
- if($hours > '09' || $hours > '17'){; v) p* _3 x. E' n* P! J3 ~6 Z( Y
. ^. F: y. T- j# T( y- if($lingqu == 1 || $lingqu ==2){//点击10点的7 a6 E1 c; {, K5 u2 I9 Z
- if($lingqu == 1){
! b2 i9 I. A+ G8 ` C - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
, n5 W9 q- c" E; V, I$ W - if($hours > '09'){$ b I0 H* [ a3 @2 o
- $num=mt_rand(25,28);//优惠券金额# k1 U# K0 O$ s% m' [+ N
- $id=1;
9 M* m" O# _) m! ]& x7 O - }- Y0 n2 {' |) U# b8 W# }# P
- }else if($lingqu == 2){, Q3 \; Z8 r3 g' }# Z5 F* r* X9 e
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
! s; ~, x8 Q4 ^( M) e - if($hours > '17'){! J& U$ W+ R( D j# t( C4 y
- $num=mt_rand(50,55);//优惠券金额2 u, V# i! o* t3 U; E9 i# S
- $id=2;
- N- O: {2 }* j: P: Z - }
1 S5 K& @$ ~( ]5 }( n - }
3 w3 c4 q1 U: t7 p# o$ J - if(!$id){
* \6 L( D# z$ Z! x - $data['msg']='时间还没到,晚点再来吧。';( e0 {. M$ H r4 U L
- }else{
1 t+ |7 |! X8 x - if($is_lingqu){% G6 q7 R( }' U% D
- $data['msg']='你已经领取过了,留点给别人吧!';5 f ~0 ?: p" v/ E4 v4 s/ I8 Z _
- }else{
" e$ z* u( t8 K - //锁表
/ i! @5 O) i1 @3 A _1 G8 f - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');5 A& \- ?& i" X) V2 @! d% g
- $active=M('active_num')->where(array('id'=>$id))->find();
) d. S* B* Y( V: k7 S% ?" {8 F - if($active > 0){
9 Z& j6 I, j, a T Y' w+ e" w! {/ v - //开启事务
# a: W& ^ w, ` - M()->execute('start transaction');
8 a: I) v% Z6 F, f/ h* x4 U; m7 N - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
' d- Q6 q u/ s$ m% U. G6 W - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));5 ~9 V" i6 P- e L& l
- $members_preferential = M('members_preferential');3 d: Z# L: P3 e% Q `
- //对应投资金额,
* v6 g2 l# O) J" k, x/ I w' w - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
( @0 K7 w! B d: I2 d# ^ C - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');; l3 G1 O8 _7 N7 B6 J# { x2 Z$ h
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
- k( \: S: @1 ?2 ^0 x; q H - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券- u( p1 }* y) P* i/ ]; R d5 D4 f' G
- if($save && $add && $add2){& H. Z! a* \# |4 s% F. n
- //事务提交
3 ^% w. F& j& e& J - M()->execute('commit');* P2 Y/ _5 K: Y7 I; r9 r9 j% Q
- $data['msg']='恭喜你领取了'.$num.'元优惠券';6 Y; C/ q, P. N
- }else{
; g; t& W$ T8 [. N* L0 a - //回滚
8 A% y5 Q) p' P& e% N# _1 p! A - M()->execute('rollback');
9 q+ J% W, C; e5 J- g e( G - $data['msg']='未知错误!';
W& G$ _- F! s$ E$ W7 F - }0 a/ y* J" b* y1 J; L7 w
- }else{
5 K& E4 j3 d# A) K3 L) M: @ - $data['msg']='红包已领完,你来晚了!';0 |( S5 I$ m0 n4 k
- }: L$ o3 N7 F! }
- M()->execute('UNLOCK TABLES');: d1 Z7 Y- a; q
- }, U- x S" L' O- v- w* X* O
- }
5 }9 l( F9 A3 g& Y - }else{
" {' c9 j) s% p9 s - $data['msg']='非法操作!';
1 m3 @. X; O0 D - }
4 p: s& J1 o. `9 h3 A4 X& ~ - }else{; G' l: x' }/ W x
- $data['msg']='还没有到活动时间,请晚点再来哟!!';9 G- N+ A V$ d% H0 k0 s ~
- }
3 E; A% l% p% c' \; I0 y' | - }; a, f2 g/ o5 \9 I) c8 h! x
- exit(json_encode($data));! Q4 `5 J( F" K. j4 n
- }
复制代码
$ a# E m3 h- E6 h9 R8 X
/ ~1 o7 u( _! m7 i3 o( M/ n |