|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 " ?; G% r+ B$ S' O+ s' z
Mysql中的锁语法:
5 s% d3 z# I* Z, i& oLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
9 z0 V* H. W2 [0 b- t: \UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表; @: s4 ^6 C+ D0 M$ S
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
) U, q& x" v. N2 _" v' b注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
* O# ?6 x( _1 x* [7 j3 g5 j文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
9 S9 n+ J: u, ?测试时,有个文件就行,叫什么名无所谓 总结:
$ r* g" w# i' j7 J% k s# [项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:9 }& |- z; L* p E4 s
1. 高并发下单时,减库存量时要加锁, W2 q+ ` J6 C& c8 b8 H
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*2 _; n! u% m: ^: K7 }7 Y4 O
- 模拟秒杀活动-- 商品100件
3 S4 Q$ Z4 C6 E& Z' C - CREATE TABLE ta, V+ t6 H% y4 c& ^- a. R8 `/ M
- (6 Q0 m' @2 w4 r! Y
- id int comment '模拟100件活动商品的数量'
2 P+ H4 i1 u9 _5 R8 q - );( Y+ ?4 h' E5 X4 i
- INSERT INTO ta VALUES(100);5 R8 ~" y# t; j
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件& H* h% G9 i4 @
- */ 5 h' C$ F, s3 X1 J: ]
-
! v/ [+ v1 q" @; V - // 关闭错误报告
0 B/ Y1 W& X; e' g+ \/ N0 B; p! I6 V8 E - error_reporting(0); $ U# W X( T+ R, T- q7 [
- / Z! R* s8 q' v. l |, g7 N$ L9 z
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
& f; c- K" @5 H+ k, z# w- ` - $dbuser = 'root'; // mysql用户名
, p3 w- J% L& `' k - $dbpass = 'root'; // mysql用户名密码
" r6 r# E/ J5 R* C( s: q - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
8 p: l$ x# r8 b - if(! $conn )
" V7 Z( @% F' ?# k. r - {
( y0 W& i4 J& H6 o# b; z( ] - die('连接失败: ' . mysqli_error($conn));
. U g" Q/ k2 t* s8 j1 r8 { - }$ u# j2 j d6 Q& Y/ ?, N0 F ~
- // 设置编码,防止中文乱码
3 n. L/ G3 E% ~: s' M$ P - mysqli_query($conn , "set names utf8");& ]3 T* S$ m& |
- mysqli_select_db( $conn, 'temp' ); ( g+ c$ z1 W5 ^; y
# p9 s, V; E) Y- # mysql 锁
- H) P$ _% P | Z' N# h - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 : h& I4 _6 k, t3 U
- $rs = mysqli_query($conn , 'SELECT id FROM a');
8 t' T; B' x: u- k# ?4 Q$ E. O! Y5 e - $id = mysqli_result($rs, 0, 0);
2 g& a z" z, i2 |4 U1 T9 J' k - if($id > 0) : p4 e/ b3 b: j! u2 V! ~0 W
- {
7 A& f: {5 [) N; v% o- ], ]/ e - --$id;
3 \ [$ E2 W7 I) d - mysqli_query($conn , 'UPDATE a SET id='.$id); 9 l+ Z! p& `5 ~& d$ i9 k
- } - X- s2 o3 T: O- }/ q
- 2 P) y2 y3 }3 V! |0 @7 a
- # mysql 解锁
$ ]* I# [0 n$ h* ^ - mysqli_query($conn , 'UNLOCK TABLES');
) f5 {( R4 ]/ t2 n6 K6 c - //查询解锁后的id值: }3 ?$ `; b1 m" _* U4 r! B" T. W
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
* b) Q D, \: @ - // while($row = mysqli_fetch_assoc($res))
) T) [# G! y1 |/ z6 S9 X( D - // {9 ^5 T& A8 ?8 w$ a) ?! |4 k
- // $id = $row['id'];& A4 M. r* u; L8 r
- // } h) s8 l$ h; @( P
- // echo $id;
复制代码
6 Z0 n: Y/ _* [% ^
/ Z7 j% y+ A) U0 q
- W. W$ O' @# M, B0 l4 Z0 UPHP文件锁示例: - /*
4 ^, T" s9 t3 S5 n% [* [; d - 模拟秒杀活动-- 商品100件; S' ?! X6 S1 I3 j2 h5 E7 @
- CREATE TABLE ta
* n: Q2 _1 C5 I( h5 Y - (+ r: t+ I; Y8 [0 q. S
- id int comment '模拟100件活动商品的数量'* G# V/ }/ S8 `6 j% x7 p9 }
- );! h% Z1 s% @* m1 g5 x) e- |' h
- INSERT INTO ta VALUES(100);) o9 j& C4 G* ?; A
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
. w [! k7 q$ @& _5 { - */
$ R: E9 l4 Y* n# H! w7 ^$ C - $ v. F6 d% W# `; _" J
- // 关闭错误报告8 ~$ h. x& j' ~# R Q4 S
- error_reporting(0);
, {# {! h, ^4 y) J
: M+ E2 E% m( X6 o! F$ d- $dbhost = 'localhost:3306'; // mysql服务器主机地址7 E: g( b9 e6 p7 u3 {& e( i9 h" }0 \
- $dbuser = 'root'; // mysql用户名- S$ ?) |% G: q; }5 O# n( @: C! ?
- $dbpass = 'root'; // mysql用户名密码
$ B& O$ M4 U/ A& C% `- C% k - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);1 {4 G- X- x$ o' ?9 f" d
- if(! $conn )
# I2 N/ T+ E3 P" t) I* r - {
+ z9 m% q; r* l! e% r5 S - die('连接失败: ' . mysqli_error($conn));
! N5 U1 A9 {: u* R9 _ - }) G! x1 u/ u, n# T' \4 w
- // 设置编码,防止中文乱码9 N8 o1 u7 a/ p5 A+ M
- mysqli_query($conn , "set names utf8");9 ^- O7 Q7 l* {0 n
- mysqli_select_db( $conn, 'temp' ); 0 Z- |9 Z; X- I7 V; w4 J, |
- 7 b8 E5 f3 b# m1 S0 @/ n c6 N
- # php中的文件锁 5 m2 y) [* h/ L7 |: Y" Y' s( k! a
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
7 j- P# S( j) F - flock($fp, LOCK_EX);// 排他锁
; Q! S. j0 I4 | - , k; ~9 |/ j( m7 ~% h
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
% A* R* b' A: s1 p1 h" C9 Y - while($row = mysqli_fetch_assoc($retval))
+ N9 K- T3 I h/ f* M0 w' K - {
% X6 l# S4 F; U* ~7 q+ H/ _( d - $id = $row['id'];
% Z+ x7 e1 F. j" H - }6 `! M, b/ U. W5 b: i% W- e6 l S
-
: j s2 H8 t z0 z. X9 X1 H/ g - if($id > 0)
- h3 R6 R4 Y- W5 z! g! v& Z- p - { $ f, L. c4 s1 y$ f5 F4 S. y4 ^
- --$id; ! b4 P! ^2 o1 l
- mysqli_query($conn ,'UPDATE ta SET id='.$id); W, c: l# O1 S/ m/ ^2 P
- }
* V$ k3 f9 t' I7 k; H, d! {: u - # php的文件锁,释放锁 O# m- b# V4 ~9 L( \
- flock($fp, LOCK_UN); 6 i, [. i4 X, i
- fclose($fp);% y6 m0 s8 k; w7 n4 L
- ! W) p1 B) ^! h S' c: T
- // $res = mysqli_query($conn , 'SELECT id FROM ta');, p. |2 N, h; z! D/ |* p9 A3 w3 B& C
- // while($row = mysqli_fetch_assoc($res)): s4 j3 t; _8 K
- // {
2 @2 `+ V7 V$ G8 u0 O - // $id = $row['id'];
7 M, b4 X: b/ @! B - // }
" V9 H- o: J8 v6 a3 c - // echo $id;
复制代码 1 t W3 i/ H2 ~$ g5 Q# `' }
4 m1 F7 V: B" N# S
抢券活动实例: - public function envelopeSnatching(){
* ^, {. c& _( a! d( O - $lingqu = $_POST['type'];
7 a, V$ n6 m+ `2 g3 i - $uid=session('u_id');//用户id
6 u; r4 ~4 J% ~ - if(!$uid){
4 z* m1 }: k) L; e - $data['msg']='您没登录,请先登录!';% N. h: F7 G$ Q6 p+ e b1 V+ [
- }else if(date('Y-m-d') != '2017-12-12'){' d$ A9 {: C% x: R/ i0 @; A) V
- $data['msg']='不在活动时间内!';( L2 m& q; l6 c8 p) t
- }else{ B! j# }: \2 I" S
- $hours=date('H');//当前小时数
. K: u( q& ~- B7 k- ^ - if($hours > '09' || $hours > '17'){; G( o! t7 e: @0 Y- ?
- * @6 |6 ~% j" p, O$ G& H
- if($lingqu == 1 || $lingqu ==2){//点击10点的
* U& \; N; o1 Q" W - if($lingqu == 1){2 t- J, i2 F* X3 w$ }+ `
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
* W6 s# O5 R3 }3 c" L. N9 r- F9 T - if($hours > '09'){
, S0 U1 k& k! V) E3 c# H; ] - $num=mt_rand(25,28);//优惠券金额3 y; T( A [- x- n6 I' H8 d2 _" q% B& l+ a
- $id=1;+ V9 j+ U/ X( b2 T* e
- }3 k! d* W2 _$ l' p' D w" n2 b
- }else if($lingqu == 2){
; y% X3 j3 p4 h4 w* \/ l - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();: O( i% r4 T4 j3 A/ M7 V- h
- if($hours > '17'){; {% {) S1 `4 I" ~$ k
- $num=mt_rand(50,55);//优惠券金额
; Q1 n8 D, p% Q1 v8 Z - $id=2;
2 ~' B& F% N9 Z1 y' s - }* A+ A% |, N) z
- }
% c8 z# e7 }8 g6 {% m - if(!$id){
8 q3 T# m( n, K# p' }- s. r - $data['msg']='时间还没到,晚点再来吧。';& M7 l& G( l9 i6 o
- }else{6 ^& K, T' h! |/ V- b2 \# ]' I. A
- if($is_lingqu){
) g |/ H4 ~, A' H: {. Q( b; x - $data['msg']='你已经领取过了,留点给别人吧!';2 M* E* [ u6 { M
- }else{
8 P7 G y" K" Q# ~" X# F. R - //锁表
3 x3 ]* w: W1 v - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');2 I0 F) y/ N" D% a
- $active=M('active_num')->where(array('id'=>$id))->find();
+ v- u: J$ o) T - if($active > 0){
8 l7 V8 A4 I8 N2 }% f- \: k, w - //开启事务
) ?6 x6 Q) B( g) A( X( k - M()->execute('start transaction');# a* ~9 Y3 G7 @6 Z* B
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
5 k+ S5 K: k% b0 e: ? f( P5 W# j - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));) j1 S. X% T4 ~! \& n; K* a2 d
- $members_preferential = M('members_preferential');4 Q8 D5 e, h5 E. H9 ^" c. o
- //对应投资金额,/ i, A9 V1 U, @, F. ^; H- ~6 u B
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));! N# w' z0 V6 p" n* v* V
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
. e. E: \1 X$ a6 [1 z! s - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
# J1 c1 Q( V( x$ H. X) @ - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
X+ u, g C7 s+ D - if($save && $add && $add2){
) }8 f: ~# d7 ]- b$ n) E - //事务提交
& ^2 v x& ~6 C+ d& m# @! c1 o - M()->execute('commit');; Z% ~% m( w( Z3 b
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
, p7 t4 O; c8 A4 _7 s - }else{3 a" t z2 }1 `3 x
- //回滚
/ E' U8 t' W' v! k( _0 c - M()->execute('rollback');
: L$ G6 ?# j; U* S# J2 [: H) @ - $data['msg']='未知错误!';1 f& n4 G* q2 J$ I
- }
: p/ E: g7 ]& u2 N" Z - }else{
, s2 y' J) R6 W( n( ]$ p( F0 O - $data['msg']='红包已领完,你来晚了!';
7 F3 K& g* P, w: j% r8 g$ I - }' q, \3 x4 f$ L5 M. |* C
- M()->execute('UNLOCK TABLES');2 d% J2 ]6 r6 X; {. m9 x# Q6 B
- }
' \5 A, V$ U h - }' M0 e: L( a) {
- }else{0 v; D& f$ r/ d# l0 e3 o9 I
- $data['msg']='非法操作!';
b0 W& `$ B- F( a* _ - }" r( u% W1 u( E. b7 W
- }else{
0 N! c7 q6 e3 b$ }% b/ O/ v - $data['msg']='还没有到活动时间,请晚点再来哟!!';6 e5 R+ v$ R0 w# {' j7 W
- }
9 N. o6 N9 j8 ?" R - }' I. o% ]" I. q9 Z. S2 [
- exit(json_encode($data));1 q' Y6 I/ @: h3 q0 r! \ k
- }
复制代码
- F- z' C" _7 M/ {- m5 R" U5 S, `6 l- g: m$ @6 l
|