|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 ; x; b# L6 @) E. O6 p5 W' q
Mysql中的锁语法:$ x; Q# U5 t( j, I, K) O, Q
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】 d) p4 e3 _ [' y
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表2 [- Y: V( [* N5 D
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
1 D1 I+ \9 U+ G6 ~* W; z+ z注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
) k+ e7 X: J, h" Y: e& z/ u文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。0 V6 F9 g F( q5 X# f2 T/ |6 @4 b9 O
测试时,有个文件就行,叫什么名无所谓 总结:, l, v) E5 M# ^! ~
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:& p, x7 b4 z* F3 S, Y. q
1. 高并发下单时,减库存量时要加锁
^! D3 P6 a( F% j1 m H- [2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
- @3 _3 R& A8 o - 模拟秒杀活动-- 商品100件
. s% | X" m5 h+ G - CREATE TABLE ta
Y/ a# o; @2 B% ]0 ` - (
* [1 w& v5 V- f- a - id int comment '模拟100件活动商品的数量'
0 Y; ~) n3 n4 W9 H - );. O' X9 ?& m* ]: l* \' J
- INSERT INTO ta VALUES(100);6 ] }- L$ H; S
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件& |) Z8 N' {* ]- P& b+ Z) C- |) Y
- */ ! Y% P k$ ]) Z
- , Q1 E) p8 Y$ ?
- // 关闭错误报告
( s2 ^$ P' W2 H- ~' i - error_reporting(0); ) ]" _$ q4 R8 U, K% ?
( r# H: |2 s, t2 j$ O/ @( E- $dbhost = 'localhost:3306'; // mysql服务器主机地址* T) x) Y5 S- m
- $dbuser = 'root'; // mysql用户名
: i9 F( ?; h& l1 v - $dbpass = 'root'; // mysql用户名密码
1 ]% X" r' t$ a; W7 Q - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);. i( t2 o1 O+ @& w4 Z
- if(! $conn )
+ r! w% f- C) F - {1 K0 R8 ^+ b$ C5 k; v5 ]
- die('连接失败: ' . mysqli_error($conn));! |: w' G F" }3 B! A
- }! d7 g4 b! v$ w4 C( y
- // 设置编码,防止中文乱码
4 W$ N( m* u- Z. b r( Y - mysqli_query($conn , "set names utf8");, q1 n$ W9 S( A& S: q9 N
- mysqli_select_db( $conn, 'temp' ); # K5 G/ D& F/ {# A; E
: o, p: ^* }% r$ J9 [, q- # mysql 锁
1 } q& R# w6 q - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
. R1 V; S- F4 l8 e+ I- c - $rs = mysqli_query($conn , 'SELECT id FROM a');
# i% C$ u, f) [" t& h& t- B% Z, ^4 _ - $id = mysqli_result($rs, 0, 0);
+ [* @! H+ W* W2 `7 J0 O- ~ - if($id > 0)
# a2 Y, {* P$ a - {
% G0 \: Y5 Q f - --$id; 8 C* s# V. y" g5 \. \8 k9 L+ c4 t: y/ ~
- mysqli_query($conn , 'UPDATE a SET id='.$id);
: A4 S8 q, e) M% n - }
5 U! k/ F2 f8 ~/ W - 0 [5 n8 s% J" ]! S2 J! a! |
- # mysql 解锁
. a, W3 d5 Z8 A' o - mysqli_query($conn , 'UNLOCK TABLES');8 \. ]. w& |+ d& {
- //查询解锁后的id值/ l" U3 m% o. _! s, i% I# a
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
. m' i1 v2 R, o W7 q2 N2 L - // while($row = mysqli_fetch_assoc($res))
+ p/ X& A' }, i' W' F6 t - // {
9 T+ C% Z# w C1 y* O- a) ?4 c - // $id = $row['id'];
G2 g' v8 V! z3 Y& W% b ? - // }
: o9 s; q$ V" B7 A7 } _% D0 l4 B - // echo $id;
复制代码 ) L4 @, U, N# j8 ^
3 L' w1 Y/ Q# K% j$ E2 I
! K: b" U7 c# s1 R; ]
PHP文件锁示例: - /*
/ x# V5 u& s; c: C! _& Y# n+ \ - 模拟秒杀活动-- 商品100件
; Z5 u) P8 _$ k - CREATE TABLE ta
: `- {, q3 H; g$ \; J0 `0 { - (. y3 }+ h8 N2 p A5 b( M$ q
- id int comment '模拟100件活动商品的数量'
$ G6 G( z7 |3 R" R9 ` - );! |/ |1 u8 C: r5 K2 ^
- INSERT INTO ta VALUES(100);
7 S$ U" ^, x' b' z - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
3 Q% ?/ K# R: @9 `7 O - */ 0 y0 `5 ^6 }: G( K P4 L
- - L, x! Q0 r4 I6 t. t
- // 关闭错误报告
# p ~1 G& L, R; W' I - error_reporting(0); . ]/ v8 y$ r0 v' t' {
( W _/ J' {+ U7 n- $dbhost = 'localhost:3306'; // mysql服务器主机地址
: f% s8 |# ^* v! f' ?, r - $dbuser = 'root'; // mysql用户名' `3 ?9 u q" v/ Y
- $dbpass = 'root'; // mysql用户名密码
7 J C7 U. ]7 D - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);. I% P; d) l2 i7 W" d
- if(! $conn )
3 A* O# R; L/ h% ^ - {, g8 l# Q1 f- B9 M
- die('连接失败: ' . mysqli_error($conn));9 R, _; s/ s. i/ g5 C& K! {0 K
- }
) ~/ O/ m" a/ a- P1 M6 M: g8 S8 u - // 设置编码,防止中文乱码+ @% C0 d9 A* N/ {
- mysqli_query($conn , "set names utf8");
H- O% k- R, b3 z, | - mysqli_select_db( $conn, 'temp' ); 4 n. x' z* \4 B8 a
8 P# k( v3 K6 ^$ s5 d X! J' i- # php中的文件锁
8 V, ]6 R& V: ^* S - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
1 \5 @7 |+ c- i - flock($fp, LOCK_EX);// 排他锁 % V9 a$ J7 d) O1 s0 b
8 X5 ]3 ~# K$ L- $retval = mysqli_query($conn ,'SELECT id FROM ta');
- _' x0 f( s) Z: \ - while($row = mysqli_fetch_assoc($retval))
5 G# @# w, _2 O' D2 u. K6 c; U - {5 L- X4 f' R; n8 _- H
- $id = $row['id'];
2 @. I* y8 a; Y3 u+ m - }
* x% m$ F% k1 n - 9 y4 ?8 s, W: B1 f3 L1 \& e
- if($id > 0) - T- f" q0 |# t5 p3 E2 {
- { ' u7 L" o+ d" i8 C4 o. |$ c! d
- --$id;
* A* d1 n/ S5 E) g# Q2 k - mysqli_query($conn ,'UPDATE ta SET id='.$id); , d$ l4 p2 l S, ^ a
- }
- N: I! h8 Q9 A1 D - # php的文件锁,释放锁
( _9 k9 A. \0 r% {! |6 J! k" \; N2 e/ ]: Y - flock($fp, LOCK_UN);
- v/ T# T) m5 \" a. h' \/ U - fclose($fp);
+ R+ [8 \9 ]" I
, f: o1 |! M' L% H* b. a6 F( G) b- // $res = mysqli_query($conn , 'SELECT id FROM ta');
; i4 |& O% t0 u' W+ P4 ^ - // while($row = mysqli_fetch_assoc($res))! M0 E2 {$ `3 \
- // {
9 z8 c) M8 t# B- l( Z - // $id = $row['id'];
* B" |- J% A% C; \- B4 E9 r - // }1 p6 [1 T7 [0 \7 {
- // echo $id;
复制代码
& _, g+ e# o2 n& k! L: S$ d- r5 }/ T0 W9 I, ^
抢券活动实例: - public function envelopeSnatching(){
9 y$ g. E4 O. c8 _# C1 s" A - $lingqu = $_POST['type'];
1 c/ F2 H. F$ S3 w$ J9 n8 { - $uid=session('u_id');//用户id
* e, T* h2 }( ~9 T( h - if(!$uid){6 ^* w1 P2 f0 d$ a
- $data['msg']='您没登录,请先登录!';% q4 U# A+ p. `
- }else if(date('Y-m-d') != '2017-12-12'){6 S& h% C$ l1 T/ g" H
- $data['msg']='不在活动时间内!';
3 a7 l) i; q3 w7 j - }else{
, U1 Y+ n5 }) B/ Y - $hours=date('H');//当前小时数
3 v. E! c. E0 W# \* A9 x - if($hours > '09' || $hours > '17'){6 S8 X2 `1 L1 P# P7 O% e
- 5 y- ], M* e, F( B6 V
- if($lingqu == 1 || $lingqu ==2){//点击10点的
; ^- M) ^ f) K5 r$ p - if($lingqu == 1){
3 N) B$ g) d3 T - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
4 R! X9 `% {. k# z - if($hours > '09'){
" _0 [* s5 W; p. } - $num=mt_rand(25,28);//优惠券金额
. |$ J; k3 @& R4 D9 a( v, n - $id=1;' B( @" g: u9 ]; V
- }" L S( i( T5 P9 K/ N7 S
- }else if($lingqu == 2){
- A* k8 U8 u. T9 V: e: \ - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();! b/ Y. Z( G( F* Y, f' X
- if($hours > '17'){. L$ Q' _: ?8 O' h) k2 {
- $num=mt_rand(50,55);//优惠券金额( x2 j4 f; n" y- O [
- $id=2;* i. @) y- P2 {. A8 W* G- j/ l
- }
. l3 q9 b+ h$ F s: x+ W ] - }
" A( e: K! V$ \2 m" [2 m0 M - if(!$id){/ t2 l$ {- D6 Y* g; y
- $data['msg']='时间还没到,晚点再来吧。';
. Z, R# |# f- i7 y - }else{1 n* p; P2 } t) w' L5 \( O+ `
- if($is_lingqu){" F! t9 x; L4 Y
- $data['msg']='你已经领取过了,留点给别人吧!';
% F1 A. q: R/ O# n% H2 p - }else{
( c ?# N0 ]; ~# j$ C; c - //锁表* [" J$ d/ F7 e' a( |' L( s/ F* H% q
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
9 y: b' C- X4 y0 L$ t - $active=M('active_num')->where(array('id'=>$id))->find();) q. G* a$ L7 d, J1 v
- if($active > 0){
2 y' B) @; t8 F - //开启事务
' ~9 ]3 S& z) C" A% S1 r7 r - M()->execute('start transaction');" K; C- J, X) F5 \) w' s
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
7 u, U& A: V% a7 |* ]3 y' N - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
# W7 K4 _0 s/ P3 b( Q& F) ^ - $members_preferential = M('members_preferential');
, E- m/ x8 W: ?5 s9 y! Z9 K. z- m - //对应投资金额,
" P6 F2 l% {+ t - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));. {1 g' t# ]4 m9 O3 C
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
; F0 L/ e$ F4 _* s2 y - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间6 T/ k$ n+ g" n# ^; _! e8 n; T
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
; H: L2 b9 V* D+ T - if($save && $add && $add2){
' z* U- z0 a9 m3 F- D% z) v - //事务提交
w, Z0 r1 k! ^7 T, R# f& N) j - M()->execute('commit');. X6 \2 ]6 ^8 F. i0 v7 V \
- $data['msg']='恭喜你领取了'.$num.'元优惠券';2 t0 a- m. v: S
- }else{
5 g- C7 s/ v, H# S: U. \ - //回滚
% d# U8 e6 G9 s4 B3 r- |; O( A6 @ - M()->execute('rollback');+ e3 M; @+ }) h$ b; |2 W! N3 \
- $data['msg']='未知错误!';
* a7 m* X. @1 p3 f0 D - }& C y1 W8 I! [2 f) l* h
- }else{
% X' M/ t% b V3 U- c# Q& j! Q4 ~/ q - $data['msg']='红包已领完,你来晚了!';# d7 X+ {2 w2 \% o( f# g
- }
A& V9 k; x. n' y9 @3 W8 k - M()->execute('UNLOCK TABLES');( a4 |6 o, K- h! [* D
- }
( \ C$ k3 G Q3 _, \ - }
( G$ ~+ N3 p. ^% Q8 H - }else{; M0 w8 A( h% M+ b
- $data['msg']='非法操作!';6 p0 r$ e8 L& Z7 \5 b
- }8 V8 B+ e% t5 {& s4 ]: T& a; T
- }else{+ P/ f: ~* Z" v A0 D" Y: a
- $data['msg']='还没有到活动时间,请晚点再来哟!!';+ k5 K, \" w7 P: B
- }
( F" q3 M8 ^5 x1 O - }
8 _& N6 O9 ~; Q+ I& @ - exit(json_encode($data));: ]. J$ ?- m+ C5 L1 w: W
- }
复制代码 9 U. N! d9 Q9 a9 e
% w ~! U4 {7 I7 R- V( S |