|
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量 - C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 . |9 F0 p- |) ^) Y
Mysql中的锁语法:! e- {# l9 @, ~, @7 G. U7 J1 D' p
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】# v" E# v8 t h/ m* I1 V9 i
UNLOCK TABLES 【释放表】 Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表5 m2 H5 _' `: B( I% e9 ]; u
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞$ w9 k+ E3 }$ \
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来! PHP中的文件锁 :
: T9 }* @% h8 {" j ]; B! F文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。/ q% E1 l2 g$ L4 h4 M
测试时,有个文件就行,叫什么名无所谓 总结:
. I! }& t. o7 ?$ [项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。 比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。 应用场景:
; F1 ~3 b! I: l5 D: j0 g/ ^( e1. 高并发下单时,减库存量时要加锁( C& E8 R/ }, ` q
2. 高并发抢单、抢票时要使用 Mysql锁示例: - /*# U1 a2 x+ w; w1 z5 t7 x. \
- 模拟秒杀活动-- 商品100件1 c# g" {* r0 i7 \. r, k
- CREATE TABLE ta
4 n) ]0 [" W6 c9 U8 o9 { - (6 p* ^! H4 S$ J$ g0 U0 H
- id int comment '模拟100件活动商品的数量'
, K1 m9 ]8 A+ A% ]- V - );; {" M5 i& l8 p0 k1 n
- INSERT INTO ta VALUES(100);
& ^' s8 r* V: D# k3 L/ z0 K& T - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件7 q' |) B h, |, B8 p
- */ ) x8 `8 G3 Y h9 }
-
7 p% j0 \' @. e. c - // 关闭错误报告! _; T# G" Z% s( X) G: _5 a K
- error_reporting(0); 3 U' M9 M# I% Y4 B
) @5 R' ~# o* D3 i8 z( G5 Y- $dbhost = 'localhost:3306'; // mysql服务器主机地址
f* V! K% E3 [ - $dbuser = 'root'; // mysql用户名4 I. w9 _+ h7 c6 Y$ V
- $dbpass = 'root'; // mysql用户名密码
5 c( F* D9 s5 y1 c6 w k8 o/ ~ - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
# ?3 o- ~) R3 x( _: r - if(! $conn ) R3 X; m) H; ?2 u7 R( W7 D+ Y" ^
- {, d( {: o6 }. V$ ~% w4 o
- die('连接失败: ' . mysqli_error($conn));9 ~9 o4 K, r* }- H
- }
; {& Y# x" x' d* B N8 P, J) D! y" K - // 设置编码,防止中文乱码* F% E0 p: P u* @
- mysqli_query($conn , "set names utf8");+ [, s. d; A5 p2 o X
- mysqli_select_db( $conn, 'temp' ); 4 C- Z) ]( P; u
4 {; A" q5 _+ k+ ^5 H$ b- # mysql 锁 0 L9 L. [9 Z* v% ?, r" i
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
% s. h9 |8 f( n% B* f - $rs = mysqli_query($conn , 'SELECT id FROM a'); 5 N% y' Y0 B" {5 F9 C- y
- $id = mysqli_result($rs, 0, 0); & X2 l7 n6 g- n3 \1 M
- if($id > 0)
& }+ e* d, r+ l, N( F* o - { ' O6 W4 V4 d% I* Y2 Z+ X
- --$id; * s7 b8 @0 L) z% t n' J- }
- mysqli_query($conn , 'UPDATE a SET id='.$id);
]# M5 o8 a. j8 [4 c. }9 U4 { - } & T# e' _0 }6 t; i' x; r2 X
- $ J$ {- z2 U; c
- # mysql 解锁
) c2 U$ n" S/ W6 m3 {/ H - mysqli_query($conn , 'UNLOCK TABLES');. Y. C% D- V& u
- //查询解锁后的id值$ S( P6 V9 w5 X
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
+ p0 k3 h7 F6 P7 C$ E- T1 u% x3 a - // while($row = mysqli_fetch_assoc($res))
7 m# c2 x" \ R: ^) a O - // {$ n* }# {: B% W+ O; B- X1 |( E5 P9 f5 [
- // $id = $row['id'];/ r/ q0 R* i- ^% N/ k( E
- // }, M7 q, B4 g3 {
- // echo $id;
复制代码 ; v" S* E2 ]8 j2 Y5 x& p
6 ]) I" L5 o$ c: n; [* H) u
( N; h( s0 P& g, @! s7 \6 LPHP文件锁示例: - /*: W; c. V; ?6 z
- 模拟秒杀活动-- 商品100件) e3 V' b( C3 w! g2 ?/ G0 C
- CREATE TABLE ta) \; ` l: F* v! V' b5 Q9 [: w
- (/ t7 |4 U r4 e3 o0 w! w
- id int comment '模拟100件活动商品的数量'
; \. w1 h) d% I9 s3 }1 p - );
3 ~ x/ c/ H1 [5 g w - INSERT INTO ta VALUES(100);
& G- @ O W* B& |$ ^ - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
6 | u+ z; p3 L6 Y$ L m - */ / A `" |0 _' M5 s7 M: F
- * i5 k/ j( S; M- G& K6 U. I$ d
- // 关闭错误报告% O$ B' q3 T# P- O$ D
- error_reporting(0);
, Q- \9 k& _- N
2 E* x( I4 M* F9 j0 Q% G i @% W- $dbhost = 'localhost:3306'; // mysql服务器主机地址+ m6 \& s9 L$ A* B- l; p
- $dbuser = 'root'; // mysql用户名
: D: Q5 ]5 G" V' t+ g# ^" g - $dbpass = 'root'; // mysql用户名密码
' g$ k5 u5 o) Z% J- A0 l - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
9 T; a9 s+ A$ Y/ z q* r! H# s6 m - if(! $conn )
' ?+ ~0 H* j! K9 D - {- O/ F7 R; p0 D0 K8 D8 [" F
- die('连接失败: ' . mysqli_error($conn));
( g2 j! c3 }, h - }
# l) f' R7 C' t f" z! i' K' f - // 设置编码,防止中文乱码
9 m; F, [' {" ` - mysqli_query($conn , "set names utf8");# M% h4 F$ q4 K" s
- mysqli_select_db( $conn, 'temp' ); " V1 W: f0 A& J' t# {( x9 N# ~
- ?& y0 O1 p% \2 F- # php中的文件锁 ; F2 [2 l: H9 L0 U
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 * t9 s* ~0 N0 K% T1 Z8 I' u) }
- flock($fp, LOCK_EX);// 排他锁
- _! R% q0 v, q' K7 y h/ J @ - 6 H- T* V# Y- H5 ^4 [3 f. Y
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
; L f1 O7 S1 l+ P' T- E' S - while($row = mysqli_fetch_assoc($retval))
* Z: [/ R# q* D. j W1 l- S - {5 `! Z; k% A6 w- {1 ^( ^$ d
- $id = $row['id'];
, l1 }! Q. x5 S9 f! D) I { - }( c& K6 G6 }9 O
- 5 d9 g" o( l, p' U9 r( e% N
- if($id > 0)
( ^. ]) W- C$ a - {
1 n! t% E) s6 L) B - --$id;
: W L R1 m+ c9 @ - mysqli_query($conn ,'UPDATE ta SET id='.$id);
, x" x) j/ @/ F' a! r r - } 8 K* D3 _6 b& |: D# C
- # php的文件锁,释放锁 # W4 z6 p( C% B9 Y* W0 `
- flock($fp, LOCK_UN);
( H/ @7 t! @# S8 @5 y - fclose($fp);
; i+ e u# \) K - 4 F; J6 `# q6 d
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
2 b; P% _3 q/ ~" z; T- g) m3 i/ Y - // while($row = mysqli_fetch_assoc($res))+ i* c: O# L0 d5 Y4 J
- // {
$ E7 [; L1 R5 k" }$ Q# Q5 G# U - // $id = $row['id'];: f$ \, \8 c; B) o1 j
- // }
' y/ L2 u0 w7 |5 ^# p% f3 j { - // echo $id;
复制代码 4 g7 W) S! W" j! {& z
. {- p# N, j: k, ?3 T
抢券活动实例: - public function envelopeSnatching(){
" q7 M5 n* X; ]! C - $lingqu = $_POST['type'];
$ b v% ?4 ?1 w- b2 J8 a4 k' @' t - $uid=session('u_id');//用户id
) _! {& n1 f) V) V1 }$ a ?$ Y - if(!$uid){- X; o7 W* H8 T5 G
- $data['msg']='您没登录,请先登录!';7 [% c/ Y+ T- `3 }% s8 c
- }else if(date('Y-m-d') != '2017-12-12'){
6 F6 q9 [: ^. t4 I6 b1 b - $data['msg']='不在活动时间内!';9 v5 Z& i# x3 k- c9 R. E4 c# R: P' @
- }else{4 m/ T, d" p! }6 s: j6 w
- $hours=date('H');//当前小时数- G1 }6 |, n" ^3 X6 v) }& `) @
- if($hours > '09' || $hours > '17'){
# d% U- O% D9 h$ o. Z - 3 x: j0 l, S( U% ]
- if($lingqu == 1 || $lingqu ==2){//点击10点的
) i8 n a9 Y7 X. m) R& Q1 ~+ G - if($lingqu == 1){. D6 }. H& T: e) ]+ F
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过. I& ]# r" s8 z w6 Q6 W) H5 k; m: }
- if($hours > '09'){
. T, ^$ E. ~; w$ a8 z: Z - $num=mt_rand(25,28);//优惠券金额" |0 n: q3 m& P) i! Z1 P
- $id=1;
. b9 p. @, J" M( t6 [) u9 j - }* R! f9 L; V2 `* i+ A4 i4 W- {; Q
- }else if($lingqu == 2){
8 n4 q' ^1 v' M+ \+ m7 {) F) Y - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
- [4 Z6 [. J4 R* | - if($hours > '17'){
0 G# _$ j1 A: I) i" Y5 m6 D - $num=mt_rand(50,55);//优惠券金额1 @. [/ x2 O. Y% p; _3 v. Z
- $id=2;8 G; B' u1 V, ?9 o
- }
+ a9 G( V) `5 \7 ]' w$ N/ k - }! u) f. r6 E% c# [6 {) B' c+ T( h
- if(!$id){
( s' Q! w Q1 \* [: B- v' k - $data['msg']='时间还没到,晚点再来吧。';) C% n9 _3 @$ a. K) v
- }else{
. L. ~1 i; z7 F) q$ u, z: W - if($is_lingqu){% N* V- p: p1 Z; R1 G
- $data['msg']='你已经领取过了,留点给别人吧!';! }) g/ ~$ @& d( b
- }else{
7 z! P3 }9 c; M: H5 ~4 n - //锁表1 P% m1 \0 B4 h2 n
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');; R- i8 w6 E6 ~1 l1 S V
- $active=M('active_num')->where(array('id'=>$id))->find();
6 J9 {# o" {- i$ m% c - if($active > 0){
. R: b9 r7 M! I3 Y5 }: F P8 B5 l b - //开启事务" f; B' @3 G2 l
- M()->execute('start transaction');
: w5 \' W7 N2 G# Q - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
5 s- y# H/ G" t! k" ~3 b5 S - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));; g& X. ?* ]' V' w* R5 x/ |
- $members_preferential = M('members_preferential');5 i8 F+ I# b5 c0 R+ {9 q4 a t+ }
- //对应投资金额,+ V0 M9 K M9 H. {
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));1 G% c6 E( v0 }4 }
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');& {# F2 i% \0 s" }' n
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间/ R. y3 |) G, u& d
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
9 d% N& T2 }2 U5 _9 c6 R# | - if($save && $add && $add2){
& a; Z0 q: e5 a1 E! ?% {8 P& j - //事务提交% l* }& i* a h) t: e2 V
- M()->execute('commit');
; l# y& ?- m ^) F0 J - $data['msg']='恭喜你领取了'.$num.'元优惠券';) s" Z1 }4 [0 a4 G7 u6 p
- }else{( x! E$ c' c1 ]
- //回滚; E/ C3 L. _ @: w% h0 I: h5 u
- M()->execute('rollback');
e# \, n0 q( g. Z) c5 ]9 H) j - $data['msg']='未知错误!';
, e: ]: x. }' X& p/ Z( D - }
" [$ d( _1 i$ d6 K- u% _ - }else{
* r: N# @* I2 H" l8 P6 o) q - $data['msg']='红包已领完,你来晚了!';
6 C C; W: M& h t+ ] - }4 Y1 ^% ^% i5 ]& s0 B' W
- M()->execute('UNLOCK TABLES');
4 a' m! k+ U4 b$ @' N/ q$ O8 B. a - } a8 U8 E# c5 j+ n* a0 J
- }8 Y- ?) a9 ]$ y8 N8 r
- }else{
% n. Z3 u1 o+ r$ j5 H& g+ S - $data['msg']='非法操作!';* D" X) C+ q% B# y2 ^+ g6 c
- }
7 U9 t" A8 ` m* j& a4 f: f - }else{ K" g; H, j6 h1 x2 W4 c" Y
- $data['msg']='还没有到活动时间,请晚点再来哟!!';: Z5 v, L ^! H, S3 ?' }& m
- }" }+ ^4 g8 w6 i" c2 {& s
- }
4 v& V6 K7 ? }5 n5 [( B - exit(json_encode($data));
* V/ o( v8 _6 R0 t" N - }
复制代码 * P7 k, @7 T+ X: Y: f, ^$ `# b
z0 O: O' k% D a9 n* m8 F |