|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
+ w* X2 t( W+ C0 uMysql中的锁语法:! I q: M6 p) @- Q* ^4 w$ B
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】" G- B0 o% m! X/ e. ?
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表; E; l2 y/ v$ E$ F5 C6 C% ^
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
+ u( @! \% ?8 P注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :: g6 \ n; W1 c5 m( O1 G$ S5 W7 |
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
) a5 N/ r: P% x% C; E7 E测试时,有个文件就行,叫什么名无所谓 总结:$ F5 V- e- T: K" S9 h |9 x
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:: t) O5 D& Y* Y5 \4 m c
1. 高并发下单时,减库存量时要加锁8 H& U* i& i2 x/ G8 i( X! ^% z
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
9 n. U" q/ G# [; y* N* A - 模拟秒杀活动-- 商品100件
6 q" S/ U4 Z. q, ?7 N/ o! J - CREATE TABLE ta
g3 H, n# |( y+ L5 e I - (
9 e( a6 P. @ Q l" e# x9 w - id int comment '模拟100件活动商品的数量'
4 Q! Q) |2 C. y; Z3 L+ H - );/ `* ^8 r" W! O: u7 E5 k$ ?. X
- INSERT INTO ta VALUES(100);
/ q9 `8 T' n- ]# z. l* W# k! J - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件; G, ?* C9 g: d; o7 b: z
- */
* f g. r! m( i$ J) ^ -
, V6 Q5 y. C# |7 ], ]* d7 V - // 关闭错误报告
% D& P3 D8 {0 B5 b4 l- h& i9 a2 o - error_reporting(0); & ?6 H: W r! G$ F$ h/ E
- * G4 a" p2 W h0 _2 Z: m, Q
- $dbhost = 'localhost:3306'; // mysql服务器主机地址7 {" |/ H3 v5 S8 u
- $dbuser = 'root'; // mysql用户名1 z& I0 K8 z5 J; `$ L) v
- $dbpass = 'root'; // mysql用户名密码2 t9 U1 u% r6 M3 f. M! s; e
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
b9 u# i" `2 x, J( r- o& E - if(! $conn )" Z3 J! R; F& I( A
- {
# u g; m7 e, H$ E - die('连接失败: ' . mysqli_error($conn));
1 v- Z" L& k9 w5 n- r! G - }
; M5 Q" H) P& @: G" D* o% ^ - // 设置编码,防止中文乱码
# c+ G+ k' Y9 {* m: T - mysqli_query($conn , "set names utf8");; ?+ p; J1 x7 P. z/ v
- mysqli_select_db( $conn, 'temp' );
1 C# f# Z- s1 I) Y0 u- W
4 k7 U$ _2 E: e- # mysql 锁
; [+ H# R j7 J* r - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 3 [& q N4 A( j* _& c1 S$ z! s' @ P
- $rs = mysqli_query($conn , 'SELECT id FROM a');
" ?5 c- y v" Q" G; x5 s - $id = mysqli_result($rs, 0, 0);
0 n/ f# F H! g6 K. E - if($id > 0)
, h( \ l0 K) U; E - {
& [9 b* x( q8 C4 r7 g - --$id;
1 C: N5 D: W) j, z; B. C - mysqli_query($conn , 'UPDATE a SET id='.$id); 0 J7 ^; H$ Q) z s& h
- }
$ P5 J" ~) e5 H5 O* n" {
- F3 ?" I4 W6 H- ]0 G- # mysql 解锁
4 k C6 h. F' @# P. Z5 Z4 ^7 L - mysqli_query($conn , 'UNLOCK TABLES');
6 X, g0 S+ E7 x - //查询解锁后的id值
9 k3 z _8 b/ V* ^ - // $res = mysqli_query($conn , 'SELECT id FROM ta');
# o* s% @/ y, }# c( ] - // while($row = mysqli_fetch_assoc($res))2 u0 a3 G. y% a2 h8 I- r& k; W
- // {
2 _$ J6 S, ]& |. u) ^ - // $id = $row['id'];
$ A9 L E, W0 Q a9 ?3 ]: B- n - // }0 P% q" u/ y% E, R' N3 C( _
- // echo $id;
复制代码 : ]3 Y. Y, b& u& K. t, U
* N, J$ h) p! D I/ [
) r% r0 p" Z+ P" g. @( UPHP文件锁示例: - /*
! U- x2 o7 Y: ]# K i. l4 L - 模拟秒杀活动-- 商品100件2 u" n3 a. h* Q. x7 J
- CREATE TABLE ta
2 h! A1 `. V$ y. C! f* @, I - (
& N9 }$ l: |& {$ I B8 h M* a. x - id int comment '模拟100件活动商品的数量' l/ p! J7 R$ g3 w
- );
1 d" H; c/ O, N" |: y" K$ a - INSERT INTO ta VALUES(100);
4 @ g# _2 n D/ \/ \( A: v - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件% n3 }) G+ e V; W* z7 f
- */
4 u. [/ U, g! }, q, G) `& |, \ - . m* t+ c5 w O" ?6 o
- // 关闭错误报告
! z. Z* _2 k( I3 z3 p! ~8 a+ U - error_reporting(0);
' M0 o4 s T5 j9 W - 1 t% r/ X1 w+ g) {5 B
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
& u- H( m, l9 w b) _9 M2 I) d/ f - $dbuser = 'root'; // mysql用户名
: o% o8 }: N6 P. x+ t* ] - $dbpass = 'root'; // mysql用户名密码: y2 e! t. F1 q+ s+ H* \
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
. G3 c- J6 X0 A, R* u - if(! $conn )' l5 A, @4 @( h3 {4 C: `+ l0 U/ G
- {
: [% C1 M4 [# N; E - die('连接失败: ' . mysqli_error($conn));# m- h. w, Z% w7 s
- }
: _* r: a" g1 Q7 W; v- D - // 设置编码,防止中文乱码+ X" h3 w6 V9 I5 F0 C1 o+ Z" a
- mysqli_query($conn , "set names utf8");
& P' G) D. R: l/ B7 E7 S6 T - mysqli_select_db( $conn, 'temp' ); $ G1 ? _! Z* P; A3 T- [; a( m2 n
- # Z3 u; z9 ?5 \( u5 ?
- # php中的文件锁 7 v8 @; W9 u- H/ @
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
, v' |6 H. a% U& L& q/ } - flock($fp, LOCK_EX);// 排他锁
3 [( q5 J7 I5 j) N0 F6 |- o) S
/ | Y6 ^, O- M- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 3 ? S* }+ v$ \3 E2 O, l8 s# O: \' f
- while($row = mysqli_fetch_assoc($retval))
t* \: Q: r+ ?$ g - {
! l6 D1 X! @: D3 L K$ Y - $id = $row['id'];
0 C2 s: i( |7 i+ {& P! \( w5 ^( L - } t) k; l3 `1 B1 A& E
-
0 W8 o( v+ L2 t% C8 y P - if($id > 0)
) Z" ]) m& K9 X1 G( n1 V7 w! x - { ; j' b* E8 L8 o) e. c2 A
- --$id;
3 C5 L+ t- q5 Q1 l( K7 p - mysqli_query($conn ,'UPDATE ta SET id='.$id);
5 m5 h$ |6 w$ C2 w# `$ y! i6 v4 L - }
3 ~2 @1 y1 @$ J6 m. W. \. {! i; s - # php的文件锁,释放锁 : ~$ J7 G! D: Q9 Y4 E
- flock($fp, LOCK_UN);
: O1 X2 e, w1 w% O3 o4 `' p# U - fclose($fp);
/ r0 T8 n; n) i
% B, v( _3 a3 p# o0 C. J- // $res = mysqli_query($conn , 'SELECT id FROM ta');/ c- K4 V7 ~4 h0 M3 ^) p( a
- // while($row = mysqli_fetch_assoc($res))4 O' K! x; [2 X2 w% t* z8 i
- // {
9 n& E9 t9 K. t% z: n! } B - // $id = $row['id'];
' P8 @7 h7 G+ z2 y. S - // }8 H1 X1 u# |0 C( {3 B4 _- Z
- // echo $id;
复制代码 - j( j8 w. _4 q
' L; _% g% E( o6 z( W& J
抢券活动实例: - public function envelopeSnatching(){
" }1 M& }% b/ c* ~ - $lingqu = $_POST['type'];
! i3 Z+ H( e: M- m - $uid=session('u_id');//用户id" |- A' t8 ~, @
- if(!$uid){
5 A# r7 p2 l9 c - $data['msg']='您没登录,请先登录!';0 x( a. D& a: s6 T I
- }else if(date('Y-m-d') != '2017-12-12'){, h. D' D. a1 }5 k# G9 X
- $data['msg']='不在活动时间内!';
+ k9 t ]9 W6 |/ K+ R$ Y; { - }else{ ^' B0 O0 R2 p8 i+ Q% M9 a
- $hours=date('H');//当前小时数& x( q0 I% c0 ?% r2 L( ?, t6 r
- if($hours > '09' || $hours > '17'){
) H- j0 D3 W6 b& c) I0 k% f - # o1 U4 u; _: k7 F
- if($lingqu == 1 || $lingqu ==2){//点击10点的
3 @- u. ^( `1 C! F0 ~ - if($lingqu == 1){
% k6 b' @. F4 H& ` - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过3 ~; n- H* c1 E- H/ t
- if($hours > '09'){
; u3 n$ ~& [1 D6 z - $num=mt_rand(25,28);//优惠券金额
8 {/ ]' t* A$ Q0 E W. y - $id=1;
2 `* h- ?- u! e s$ s2 A - }
- P& }* F6 J. F; E+ D; ` - }else if($lingqu == 2){
?3 S2 ]+ a0 P - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
0 S$ D/ o8 P& T - if($hours > '17'){9 @3 m6 D: v0 c9 {- m
- $num=mt_rand(50,55);//优惠券金额
' Q1 \( | K! Z" w8 V - $id=2;
8 r" W* U8 f, H2 k. |8 I - }
" i! @* V0 @. |) H - }- V) U1 _1 O5 W& i
- if(!$id){
4 |8 g1 l% U& y. [ - $data['msg']='时间还没到,晚点再来吧。';; [ Y1 y* V$ _1 ]# \% I
- }else{5 l. n- ^: Q/ o% u+ R' E5 d
- if($is_lingqu){
7 J' s( U3 n! C8 O) ]0 ^ - $data['msg']='你已经领取过了,留点给别人吧!';
) p9 `" @! z& |5 }9 }1 z8 t - }else{
$ H: t2 \. O. g D% q: Q8 S( t& ] - //锁表
% S8 v+ U9 a: Z' l2 z - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
: p6 \' M+ s! E1 D1 ^ - $active=M('active_num')->where(array('id'=>$id))->find();
1 H" o3 j% P a* s - if($active > 0){
3 M% k- ]2 ~. |& f! L/ K9 M - //开启事务, e2 a: [2 p+ y* Y3 p
- M()->execute('start transaction');# D: f) F- n, {
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));7 I# z, k( }$ V; B
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
4 _: F. T$ Z4 i6 x8 ? - $members_preferential = M('members_preferential');# t7 _$ v& l2 Z! N! C! U
- //对应投资金额,7 m$ V4 }1 C, L( X9 E) e. Z
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
8 p: m3 w% D, Z4 Q& \' c9 f' N) _ - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
M/ \# k+ f+ J0 l( [ W1 I$ O - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间5 }5 S5 ^9 _% l) S* V& F9 ~
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
/ O) n; V7 L1 L, W2 D: j - if($save && $add && $add2){
% h; [$ G# ^! W4 `; v - //事务提交) Y F! u% J) H- U
- M()->execute('commit');
, U2 w5 \; |: o" _1 _4 j/ h1 P) G - $data['msg']='恭喜你领取了'.$num.'元优惠券';8 G K/ _- H7 I
- }else{6 {2 @( L# }' G8 p% l
- //回滚) J) G( h$ U4 f& g6 s
- M()->execute('rollback');( M4 B3 D! J3 x8 l1 R$ r, ]- S& {
- $data['msg']='未知错误!';
9 X' F. Y! p3 P x( { - }1 D! F, B4 ~" E# O/ k
- }else{ |) U2 U4 h+ K
- $data['msg']='红包已领完,你来晚了!';. d7 ?2 e, |$ Q, w6 z
- }* N! Y' j4 O/ I$ p! e3 G7 L4 K
- M()->execute('UNLOCK TABLES');* F! c5 Z! {3 i! a. C( ]
- }" p8 i C- t; I5 U# `* a1 H, J
- }5 L: Y! D! X2 I4 [0 s9 w
- }else{
* S: o. C9 V+ @" I3 R2 L) k. k - $data['msg']='非法操作!';
% G/ v' I% z* E. g - }
- V# f6 n/ H/ e" o- C4 H - }else{9 N- S- Y* Y+ Y; ]' k0 L" j+ e/ N+ D
- $data['msg']='还没有到活动时间,请晚点再来哟!!';/ v' g/ i" N2 L3 U4 H; s& g
- }; F; W/ _! Y3 N; C: f0 i: L
- }6 \' J( X0 M# T9 T$ M
- exit(json_encode($data));
; b) o. E6 N& q+ L+ \: r - }
复制代码
, A/ ~ \7 t1 T4 m3 ^5 H" o5 t7 p# ]" [
|