模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码
4 G2 y2 a" q# `Mysql中的锁语法:( O6 `8 S' A& ?" p( J4 l( V- \
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
$ E2 S5 T8 Q" h: D. kUNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
7 f7 ?; @, W4 f$ P, ?Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞* m/ `% T" |; p( {
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
" t/ _& i0 D& c3 c" t- Z文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。7 @+ x/ ^! J8 A7 r) Y6 R1 f
测试时,有个文件就行,叫什么名无所谓 总结:' a" [; J/ r3 Y+ O3 V
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:" p1 F i1 v) M2 o; J5 M K! ]+ w6 Y
1. 高并发下单时,减库存量时要加锁# B- S: o; |( ^
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*
, K [" b( S& F" V/ |9 J - 模拟秒杀活动-- 商品100件
/ y1 }6 r- }" n$ J+ S - CREATE TABLE ta
$ }3 { I+ \0 D8 f+ [ - (
0 x4 [& U6 Q0 n+ u - id int comment '模拟100件活动商品的数量'
% |: _' ?4 O2 p7 n! r8 I. t* ~ - );$ s( ~" g, q0 |( s6 T8 ~
- INSERT INTO ta VALUES(100);
2 ^3 W# z# z! I, F - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
" ]. [) b" B. m+ Z- G, W - */ 1 L) d4 n. _+ m* g4 E& h
-
- W3 a/ o& z8 ] - // 关闭错误报告9 j/ `7 r8 q7 i0 x' [1 e0 j! |
- error_reporting(0); h0 {3 X7 N2 Y! C8 R% S1 n( N
: Q* S1 g: a% d) ]% Y1 I* L- $dbhost = 'localhost:3306'; // mysql服务器主机地址 ]3 |4 A. z3 |: u- ?
- $dbuser = 'root'; // mysql用户名
) i. ]3 t$ x' ^6 ~ - $dbpass = 'root'; // mysql用户名密码
# {% `5 K7 A9 `: n. w* L6 T2 c - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);4 E: ~+ X1 z$ u0 \6 {& P
- if(! $conn )) |1 z7 i; H/ G& S
- {
7 I, P1 `' l+ {/ V& P6 x - die('连接失败: ' . mysqli_error($conn));1 a1 s" ?- P& l
- }5 g3 L& j, l/ ^& q
- // 设置编码,防止中文乱码
. y" [- }* t6 n/ s) I6 \9 G4 A' w, u - mysqli_query($conn , "set names utf8");9 e7 s4 q* a- ~- H3 ~0 \' D& q
- mysqli_select_db( $conn, 'temp' );
. k0 n8 P4 R# }( @* }% ^0 |0 q - # b' G- {6 o1 S$ l- b1 a
- # mysql 锁 5 r8 o5 q Y; l( u4 w
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 ! J- L+ U- d# D" c" M$ P
- $rs = mysqli_query($conn , 'SELECT id FROM a'); 0 c/ Z& ^, N( w: t
- $id = mysqli_result($rs, 0, 0);
& m- u" b% [% y+ q( }( u6 e" O - if($id > 0) 8 _' J$ A8 b1 B/ W& M3 _
- { ! y$ y# J- w/ D$ I$ J: s
- --$id;
6 N, s# Z+ X( n! i) ~/ m - mysqli_query($conn , 'UPDATE a SET id='.$id);
" O& k: J& t3 \7 M' ? - } ; X8 L' p, }2 X2 _, i
- 7 D. ]& A4 u* ?6 v! T- R/ H$ a
- # mysql 解锁 . ?2 M, L9 l5 j: \
- mysqli_query($conn , 'UNLOCK TABLES');
" P' y' r0 s/ p" ~5 T+ ]6 t - //查询解锁后的id值
; E$ D) m$ a! V9 k# m/ |8 i/ G - // $res = mysqli_query($conn , 'SELECT id FROM ta');3 t! J2 M! ?$ E8 |/ `
- // while($row = mysqli_fetch_assoc($res))& Z: W' R& r( A J+ i1 J( c& i
- // {
4 k7 ~5 A7 ]$ x) y l" Y - // $id = $row['id'];/ [& ]0 v n5 `9 V' N, D
- // }4 Q1 H5 ^* G ]% [8 e
- // echo $id;
复制代码
# j$ c, _, j% _- E/ r( ~' k6 C) T- U9 |/ b0 F$ h
# W# Q" A$ N% R: |
PHP文件锁示例: - /*. O! o9 m0 b( j) S# D+ m7 H
- 模拟秒杀活动-- 商品100件9 q5 r9 O/ H: F6 ^8 P0 Y5 d
- CREATE TABLE ta9 Y6 ~( c# k0 ]! q
- (
: U. q1 c8 b" Q: P) u+ y! t - id int comment '模拟100件活动商品的数量'6 s. O% B L6 {+ R" ^; r
- );( `& F8 K2 R( f0 ]4 d$ H$ y
- INSERT INTO ta VALUES(100);1 ]+ i* I" U/ w8 l+ n
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件; {4 O# W6 u4 a* f
- */ 2 z5 I& d* p5 F2 S
-
( V+ L# G8 n$ S, ~ - // 关闭错误报告6 z/ `/ d [: h$ F8 f
- error_reporting(0);
7 c' [' {3 g/ `4 m
: k" V$ j- `" W9 s- $dbhost = 'localhost:3306'; // mysql服务器主机地址- n9 q8 F3 G. x, D" a
- $dbuser = 'root'; // mysql用户名
/ P m' R) O- h* s Q - $dbpass = 'root'; // mysql用户名密码
! U+ K4 ]0 t* c& V - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
* U8 V: Z3 K! g& `1 a - if(! $conn )! O! A0 d# h& p* b- [" z: I& ?
- {
$ w9 e- A2 O' G( v/ o3 v: j - die('连接失败: ' . mysqli_error($conn));0 g7 y/ H, }+ r+ B' {. C8 b. y" T
- }* x$ x1 S8 X2 z& s; Y2 b- n
- // 设置编码,防止中文乱码3 v8 k3 L# C) X! \& t" I& u: S6 H
- mysqli_query($conn , "set names utf8");
. a# O% g) t/ z: q4 G+ @ - mysqli_select_db( $conn, 'temp' );
9 i6 K: D& O- w
' b' N( s* ^) U# v) f- # php中的文件锁 # h, D ? {8 J4 H+ [
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 3 O9 f8 ~% }& T$ S$ Y+ ?. ~3 a5 j
- flock($fp, LOCK_EX);// 排他锁
" B0 _; T6 f9 m- X6 e
& t3 V! X4 y4 l' h1 M0 Q- $retval = mysqli_query($conn ,'SELECT id FROM ta'); ; R1 R) I5 h2 p$ y" ~7 C/ U
- while($row = mysqli_fetch_assoc($retval))
5 {6 f" O' B- x- V9 y) X9 R* ?2 N - {! U0 L7 x4 {/ [5 ~: G7 K
- $id = $row['id'];: r. m; a7 I) s9 p2 N
- }& N3 V& ]' U5 H+ t
- ) C* P) h2 T* F7 v% c" B( [3 q4 S
- if($id > 0)
! i, P- Q3 A. |" K5 W8 Q - {
* R9 B- @7 s5 R$ v1 l' U" a - --$id;
+ x, G% I6 s8 G( [2 O4 U& l - mysqli_query($conn ,'UPDATE ta SET id='.$id);
. c) \9 P( w6 ~# u9 n - } ; }1 o" d$ j# D2 g8 ]8 T" C5 m
- # php的文件锁,释放锁 0 m( B k0 x3 h) P
- flock($fp, LOCK_UN);
! W+ s1 u% a" E3 u9 s - fclose($fp);
, w4 G. z @4 {( M - + ]# B! }/ e# o. d; Y6 ?
- // $res = mysqli_query($conn , 'SELECT id FROM ta');, G0 Z) P$ m& }# C: R. u
- // while($row = mysqli_fetch_assoc($res))
- P$ k2 H1 s( z - // {
" M2 I" t( q, m8 u% H4 a' j5 C - // $id = $row['id'];
& B- @/ _" K* x. h4 N h3 `- l - // }
/ D' k! R h/ {$ x) ]' [9 m7 k - // echo $id;
复制代码
3 A9 q+ b" m5 i- o( a4 A4 x: b! n% K. E' @; j u4 l' F
抢券活动实例: - public function envelopeSnatching(){
: q, `# g; _. V) {- c! Z - $lingqu = $_POST['type'];
: q" i# q. t8 k7 y0 } - $uid=session('u_id');//用户id& p5 ~$ {/ o: F' M* U) X
- if(!$uid){2 Q4 g# O0 U& u/ Q
- $data['msg']='您没登录,请先登录!';' O! ~! O* O5 h0 t7 u! {" E! \3 N0 B
- }else if(date('Y-m-d') != '2017-12-12'){
, M% V" }! C7 e - $data['msg']='不在活动时间内!';
* u3 c' o$ E) N- H8 R" Q - }else{
& w, ~7 e$ Y: a9 P/ H - $hours=date('H');//当前小时数" e4 c3 w1 W8 [7 G: H
- if($hours > '09' || $hours > '17'){
5 v0 u5 h( \- \ O7 T# C - 0 D. m s# w3 }* P# i
- if($lingqu == 1 || $lingqu ==2){//点击10点的
* W% \/ c2 x! ?5 `1 q - if($lingqu == 1){
- z4 e4 A) Q0 h( W$ s, D( [ - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过8 k2 o$ P1 F0 T% w7 h" R6 ^
- if($hours > '09'){
; L7 j9 L; w$ m9 u& b - $num=mt_rand(25,28);//优惠券金额
$ J* N3 {. R$ _* t! A# Z& v - $id=1;' G: Q8 v/ t* k8 L7 {# n
- }+ J1 _' I4 C* v) L, f- ]
- }else if($lingqu == 2){) W7 ?* ^' _7 L" c' H1 _
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();- r6 I, T& X3 K0 c! _( Y' \
- if($hours > '17'){
* G& l5 D& @+ C Z. O: `6 Z$ | - $num=mt_rand(50,55);//优惠券金额4 r. \; |4 P! c
- $id=2;$ y# c$ b7 a: h$ O0 ?
- }. W( ^/ |' w5 y
- }: @ |5 g- r8 ]+ R* z) i
- if(!$id){
0 A' t2 b$ z5 x1 P3 i9 M" b - $data['msg']='时间还没到,晚点再来吧。';" S/ ]/ [- h. [& W
- }else{
3 r0 t( Y+ Z0 [. i* [+ } - if($is_lingqu){
^5 K( x3 C/ b" h7 t - $data['msg']='你已经领取过了,留点给别人吧!';
0 `8 K$ |- U/ q k O! ~1 v - }else{
$ x5 M" L; p( m2 q) F2 Y - //锁表
% ?9 W3 v7 i4 ^- r+ N - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');1 E! L* ~& Y: S. h# k! h5 i
- $active=M('active_num')->where(array('id'=>$id))->find();
% j5 e9 l- G/ h) Y# t% \ - if($active > 0){
1 P, E5 [) D1 h5 e - //开启事务 H8 F8 ~4 E6 l+ {
- M()->execute('start transaction');
7 x; e; G/ u8 I3 A7 A1 q7 j* j - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));; E& Y. n* m" L. a! o. |4 e1 F
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));& G5 A. `' v. T4 _( |
- $members_preferential = M('members_preferential'); J& p/ p G8 o) H0 m
- //对应投资金额,8 Y! l7 W2 `* ^% ]: `1 e" k
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
. k; c; U+ V( v, _ - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
, L$ J L* {% F. [/ {+ P" A7 E - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
) h; u( d I6 d/ X - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券; S( k; Y. {* [4 s: [
- if($save && $add && $add2){$ ~; _9 r& X. T) q5 c2 o4 Y& x( H
- //事务提交, P+ ~2 w4 P* h* X* h1 @
- M()->execute('commit');! a* g/ q4 J {. E( r4 U( p
- $data['msg']='恭喜你领取了'.$num.'元优惠券';% P* u1 e$ {, O' Z; d8 ]
- }else{
* u" N0 p7 X0 B0 w% h+ C/ H - //回滚
+ | z8 X$ R5 F& n - M()->execute('rollback'); m9 m$ I( {; J% d6 W
- $data['msg']='未知错误!';
: F2 y7 x+ ` ~. S7 b - }
5 B# X4 B' v( w% H" a' C - }else{* ], g! k% f+ d
- $data['msg']='红包已领完,你来晚了!';* O$ y. n9 e! x0 A( z( e3 S, ]! W
- }
* {+ k# r. |4 d - M()->execute('UNLOCK TABLES');
+ z2 I% E( D0 m8 \4 [ - }; t- j% n, x U% E @* ]9 e
- }8 n2 l% Q2 V3 j0 N
- }else{/ O5 D2 m' u4 v- f
- $data['msg']='非法操作!';+ N# \' P) z$ e
- }
, w9 A- Z2 ^7 Z4 k* ` W8 u - }else{
0 r! E+ I7 V# R0 m9 I/ q" y$ ^ - $data['msg']='还没有到活动时间,请晚点再来哟!!';
- b2 v4 f0 Z* s1 p8 f! G1 d - }
4 t/ }3 J; t% C6 }; }4 x- J - }( l/ J. T7 p2 F2 y e9 Q
- exit(json_encode($data));5 l7 \( X6 K" \1 \$ x9 ]" l
- }
复制代码 + ?. f3 f( j% k8 O3 W0 o1 \3 f4 F" g# p
3 w, e4 f3 W+ n4 ^ c: v5 B |