|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
( c+ F0 U$ l9 }7 @Mysql中的锁语法:2 `( e# r6 k7 j' B. c9 x8 }, J+ b
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
* y9 Q7 x- U5 C* R* {- kUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
% H2 F6 M; v/ O ?" M+ yWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞- G. W/ H, t/ a/ C9 `! p
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :) h6 U* U" [( I% U
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
" Q; Y! D( T* Y/ \4 S( o( t) b测试时,有个文件就行,叫什么名无所谓 总结:
: I' |6 [/ V4 J; e项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
* Z& k$ F4 h9 ?0 X9 e1. 高并发下单时,减库存量时要加锁" X* X" a( U- R+ R4 @% d
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
* J+ }) m: p; d9 l4 P7 ~3 g - 模拟秒杀活动-- 商品100件
# J" ?+ `* j" j7 w - CREATE TABLE ta
( t2 Z- E' ~1 [ {8 e' S# ? - (/ z$ e. u2 |. o7 j) J
- id int comment '模拟100件活动商品的数量' |2 F% K v* M4 g
- );8 W- I% h4 D- ^# t4 P' m' d
- INSERT INTO ta VALUES(100);, {. S( K+ V- T4 x
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
2 u# L7 w( O" o7 n. y8 C - */
7 ~( k* [5 O8 t$ D% o( A+ S -
5 q- i% p# q6 M! B/ t! ^( ^ - // 关闭错误报告
2 p; ?- B2 H, O& N7 ~. Y - error_reporting(0); ' B q; ~7 d$ L* J- V8 B6 V$ I
- 3 u' v+ ^. l2 O1 C- Z8 N3 c- J- S' V( O
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
: m U& R1 b& L9 ?% W9 o - $dbuser = 'root'; // mysql用户名
# Z" z$ U3 o, b4 f - $dbpass = 'root'; // mysql用户名密码
: \7 t0 S. I. ~' G% M - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);* @. K7 n# y; J) G/ j) f
- if(! $conn )
$ |7 ^7 t) }' `- |0 ^; F1 R - {- t2 N+ q( U0 l
- die('连接失败: ' . mysqli_error($conn));1 V) K, z! f: R' i4 @5 l1 ^
- }
: j* d K3 o# [7 H/ c" m - // 设置编码,防止中文乱码0 V' q7 p. ]* E- S$ q
- mysqli_query($conn , "set names utf8");
# c& ^0 U M" n- K+ M E2 P - mysqli_select_db( $conn, 'temp' );
; |9 L# n$ _1 v) _) ~ - 1 U3 y" d+ G Z |* [3 |3 Y1 ?# V
- # mysql 锁 5 l4 q% H; O( @; U7 b3 A/ }
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 3 w1 }) R$ y! v% W
- $rs = mysqli_query($conn , 'SELECT id FROM a'); , `; U7 \. v( J' x0 I; y! G; c
- $id = mysqli_result($rs, 0, 0);
/ ?6 |' |% A+ V0 Y/ b: T: H _4 @ - if($id > 0)
6 W6 ]+ T& h- E% B6 a - { * J! f. y: |8 r4 m. |4 }
- --$id; 3 c0 r8 z# X9 v7 W
- mysqli_query($conn , 'UPDATE a SET id='.$id); Q) u3 g0 Y g7 J' q
- } + g& A# M$ t* \
5 R; ]; U3 p5 h" f" U& q/ l( q) Q2 c! @4 l- # mysql 解锁
H! v" D1 T5 | - mysqli_query($conn , 'UNLOCK TABLES');
* `; O8 P: o7 @; D7 a9 S G6 | - //查询解锁后的id值
|, e7 Y! L K7 F - // $res = mysqli_query($conn , 'SELECT id FROM ta');4 l: }. M+ B% c& ~* Q3 Z ^ _- Z# m7 P
- // while($row = mysqli_fetch_assoc($res))( I* ^1 |. j/ I, H& p' ^1 w8 G
- // {9 n3 v+ Z: k( e4 B
- // $id = $row['id'];
) a/ o3 D k- | - // }9 a/ s$ k* t7 t: f9 ]2 W
- // echo $id;
复制代码
& \- a+ ^- P3 G, b" i' `
5 ~0 g) v+ A; }% L: ^. E+ p6 b* P, L, s
PHP文件锁示例: - /*7 O9 W1 G4 x$ V1 U9 N
- 模拟秒杀活动-- 商品100件
. F" J" R- X% |$ r$ x5 j - CREATE TABLE ta
* H b; X; x) U# I - (
. P% v- n4 ~4 L* M# _6 G9 [1 U0 R! v - id int comment '模拟100件活动商品的数量'
$ \5 H h5 c$ z- t - );' I* i: s0 F' ~9 v
- INSERT INTO ta VALUES(100);$ w2 J4 B0 v/ A$ b
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件4 \( D0 g: e) v, Y6 r
- */ 7 ~1 d" _$ W& l: r2 K) E' j! C
-
8 b/ i9 h: k6 N( x; h - // 关闭错误报告
/ }% g. k, n6 ?& m( X- x - error_reporting(0); - ]. j% q; l$ ]5 q
5 ^: H+ ^+ D: y, j; u8 E$ B- $dbhost = 'localhost:3306'; // mysql服务器主机地址
1 |& G( ]8 R0 R, g6 h - $dbuser = 'root'; // mysql用户名2 l6 Z5 n6 a6 } M1 _3 h1 e2 y
- $dbpass = 'root'; // mysql用户名密码
. q7 n" L# k" A. k$ |" o7 u; w - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);5 H: B5 x4 ~; Y2 H* n2 [7 Q4 }
- if(! $conn )
5 A8 J" ^! o. r$ z# Y) d - {
" q8 ~& q1 p2 h; P1 k9 W. w; W; N: o - die('连接失败: ' . mysqli_error($conn));
! \2 M6 e, [- w2 s% e( ?: K( P) i - }
0 \) x0 K1 p( | - // 设置编码,防止中文乱码
! R; u8 w) L$ B% { - mysqli_query($conn , "set names utf8");
1 N0 V% @; u. Y& X - mysqli_select_db( $conn, 'temp' ); : K1 e, R# i: M: g/ y$ A8 S
% h8 \ W. G/ e0 x2 M) y- # php中的文件锁
% |) C. G, F' n, ^ - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
' ]8 s3 a. I1 p5 S - flock($fp, LOCK_EX);// 排他锁
* q& y% V8 X5 D0 D - * h$ ~+ E9 C3 b Q' ?$ ~& d
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 7 q( B- A4 v" C# _( _; L
- while($row = mysqli_fetch_assoc($retval))( ]! A9 k. G! |2 A: t
- {
1 w) H$ X/ O' Y+ l - $id = $row['id'];
7 O: g. S" l B I6 R& _ - }
: G* x5 r) C a% Y9 J - + p7 z0 F. A5 r. r" k
- if($id > 0)
( x4 T7 C3 Y7 r! U7 a3 w - {
# J+ d' |/ F! y. U4 P - --$id;
2 ~8 w; y7 L& p5 S& s3 \9 P! Z/ { - mysqli_query($conn ,'UPDATE ta SET id='.$id); $ p4 o% v9 Q6 d- y3 I& A& M
- } 1 w4 B( B) D( j
- # php的文件锁,释放锁
& c) P* O( w: N$ b9 J* U - flock($fp, LOCK_UN);
! b d8 t7 \ }) a6 b6 @ - fclose($fp);" l5 t# \4 i! X8 J I# Y
- ! F; r1 Q) z0 F
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
$ r) b$ U& D: p$ o6 C5 B6 J - // while($row = mysqli_fetch_assoc($res))
q0 O# U$ H! D% x, [3 E% H - // {
7 o$ {8 k% ?& S5 X( V4 g' G - // $id = $row['id'];9 y; i; V% g6 y/ {
- // }
9 |' [- C* q2 x5 F8 V - // echo $id;
复制代码 * y/ u! }4 {$ O
, u8 S# `9 q3 g. M% E
抢券活动实例: - public function envelopeSnatching(){! u4 F% C' Q' o6 t( ~/ z& f
- $lingqu = $_POST['type'];
* F0 P- O7 q Y - $uid=session('u_id');//用户id# h# L- I$ w6 G$ M$ y
- if(!$uid){( ]8 S5 {" K b
- $data['msg']='您没登录,请先登录!';
/ h5 V& w+ ]- g - }else if(date('Y-m-d') != '2017-12-12'){$ V9 y/ M2 p+ I* @9 s% O9 ]
- $data['msg']='不在活动时间内!';! y6 ~/ o. b0 f/ S, a6 m, W' [
- }else{
7 J& v( \! K/ v& J6 O, _- z - $hours=date('H');//当前小时数7 K4 w4 u- X$ ?# T3 L4 T
- if($hours > '09' || $hours > '17'){3 C9 C4 z9 [5 ^9 c6 d3 [
- ~) j6 z2 s5 g( e8 w0 j- if($lingqu == 1 || $lingqu ==2){//点击10点的' \( Z! n1 P I. d x
- if($lingqu == 1){2 o4 T3 }( m% e% J
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
9 c; a8 T0 l5 L3 U" g g+ q9 u- G - if($hours > '09'){
! {, P- z$ X, w, R$ l2 U4 _ - $num=mt_rand(25,28);//优惠券金额
N% D' l2 M, W7 Z, q4 e: L7 m2 }, z& Q5 U - $id=1;/ ?% q, N1 T. K3 F; `9 N& r$ Y
- }5 c+ u% P: S) c6 E4 ~* |
- }else if($lingqu == 2){
! b' A7 O& {0 h3 }1 y - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
' \* Z6 E2 U! s5 I K5 b - if($hours > '17'){
0 p- o" l. O* R/ [ - $num=mt_rand(50,55);//优惠券金额
9 \5 X- r, _- o$ J+ q' y0 g - $id=2;; F. D, V" M8 C$ d
- }% |# X) ?" n# i% F( F* H/ x
- }
5 B$ K/ K( h; C$ Q' Q e: L+ e' N - if(!$id){ K. K# J" ~8 k, B9 I- `
- $data['msg']='时间还没到,晚点再来吧。';
6 b: N6 _* N3 L% s# y) X - }else{
) |1 R6 v" m% Q* ^' _8 Q - if($is_lingqu){8 ? h) H$ `5 {4 j$ O
- $data['msg']='你已经领取过了,留点给别人吧!';
9 q4 O+ R m1 J: I8 U - }else{
2 _! |& E2 |) z4 C! X - //锁表+ }, F' [2 D' y1 w5 Y
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
! A. O$ e( d( ?) {; [2 i - $active=M('active_num')->where(array('id'=>$id))->find();
/ r& H; A( |; @6 `) ]" W - if($active > 0){
) j! d7 H+ B% B; Q; I - //开启事务
( T3 Z) ]( T+ h8 ]* x" w - M()->execute('start transaction'); A7 e" V. [: S8 Z' g
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
2 x; k5 Y6 T: t4 C6 L - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
4 A% Z3 b) [, v+ W5 p5 P% p - $members_preferential = M('members_preferential');
: E# H. O# N* f5 m, W - //对应投资金额,
7 O+ ?& D( w# S( u0 h& Y - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
! L! v5 C7 _7 ~0 t" z - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
% [2 h# n+ R3 F2 i! Q. |1 n - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间+ E( A% E# x3 [1 j% M
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
; |4 {6 A6 _2 R, n - if($save && $add && $add2){
" p( ~3 v: K( `5 G" G - //事务提交
* i- [! D0 |( m3 F( a: e - M()->execute('commit');
& H' p* J: i' E - $data['msg']='恭喜你领取了'.$num.'元优惠券';
/ O: [ N0 _+ O! i1 F, m& l - }else{
0 v! f, X( f2 j* o; X. M' l$ B - //回滚8 `$ p' _) T) U, [
- M()->execute('rollback');7 i( p, ~( }8 ?* W+ ~6 O
- $data['msg']='未知错误!';4 y% Q+ H9 x& f/ r7 Y y; T
- }
K" Y: ^) K, p& p! D% X- i* ` - }else{
4 L* `/ m7 k$ X - $data['msg']='红包已领完,你来晚了!';$ S* Q. Y! {! B7 s5 |; A) [! j
- }
; x5 H$ \/ x) ?# x& Y4 l# ]3 ^5 I - M()->execute('UNLOCK TABLES');& u' F6 R" C' {
- }
- Q U' U4 T: L - }
8 T9 i3 B- x% T - }else{
! X, H+ f* j( S8 Z; p0 j - $data['msg']='非法操作!';
3 ^& N# u& C0 \6 b! J+ d - }
e- u1 G+ s4 }4 c - }else{" M$ O2 k' C& _; A: T1 M
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
9 L3 o; v1 j- Q" K2 K5 w9 N2 u7 z L- } - }# K# s/ a0 Q& _9 J$ K! W: x
- }
$ Y7 e2 [, X# n1 X* S6 p% ~; X - exit(json_encode($data));
) M7 l; [: o& X - }
复制代码 0 ^, G/ v1 I3 n
j& n* _' n; p8 _
|