cncml手绘网
标题: MySQL(表锁)、PHP(文件锁)锁机制及应用场景 [打印本页]
作者: admin 时间: 2022-3-17 15:53
标题: MySQL(表锁)、PHP(文件锁)锁机制及应用场景
模拟高并发访问一个脚本:apache安装文件的bin/ab.exe可以模拟并发量
- C:\phpStudy\Apache\bin>ab.exe -c 10 -n 10 http://localhost/try.php // -c 模拟多少并发量 -n 一共请求多少次 http://请求的脚本
复制代码 1 u0 s4 B9 V4 M# H
Mysql中的锁语法:) ]' q& a- d5 W2 Z$ N
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】8 n$ B; u1 V6 }0 C
UNLOCK TABLES 【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
2 \) g6 W, g p$ U8 g! ~* N* _- x2 VWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
2 m) ]1 B" x9 d' j+ B# b. f注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :+ j2 }* E( ?' b$ m
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。4 a9 t# B9 a5 _# Y! V( y6 Z8 x3 M
测试时,有个文件就行,叫什么名无所谓
总结:
4 |: l$ _# V1 M2 W7 p% p项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:1 o( w; C% n" {# e* |5 U3 K
1. 高并发下单时,减库存量时要加锁9 T* I* t r; V& t# _) f0 U
2. 高并发抢单、抢票时要使用
Mysql锁示例:
- /*
" _1 ]( L) U. U. c( f5 S4 L - 模拟秒杀活动-- 商品100件4 W8 t" ^- W# V2 ~7 X
- CREATE TABLE ta. L+ j" W% w% ?* o4 I
- (
4 X! a. m* }; X7 r - id int comment '模拟100件活动商品的数量'
( T- ]' \: v3 ~0 l - );2 L3 ?8 X+ k$ M V
- INSERT INTO ta VALUES(100);7 z/ L% X# w5 ]3 }/ e
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件9 Y) h# R2 O2 E0 Y7 B- L
- */
8 M# Z/ D* _" f" N! j - " p" M6 R1 F h) s, F
- // 关闭错误报告
: N3 P2 n9 x/ B" u - error_reporting(0); 7 m2 i0 j; F* U
% w- C5 j; P! Y2 l( N) w- $dbhost = 'localhost:3306'; // mysql服务器主机地址* i/ l2 D4 I2 g9 T& }, G
- $dbuser = 'root'; // mysql用户名( L# A& d& s+ [( p
- $dbpass = 'root'; // mysql用户名密码, O2 H" \+ {! d u
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
) `& e9 \% L# K }. F4 ?! X - if(! $conn )
! C6 v" l# { I8 i - {: T! D' S9 W$ k
- die('连接失败: ' . mysqli_error($conn));
* H% \0 U) I+ S) o* n - }4 @5 `4 k7 ^- F8 _$ f6 V1 j4 F, r- U
- // 设置编码,防止中文乱码. C( l4 M/ ], Z* C) I
- mysqli_query($conn , "set names utf8");
3 T# [& M' q; Q8 \% z0 @ - mysqli_select_db( $conn, 'temp' ); # j9 ]7 x" i8 C0 }+ y
8 ~8 n# s! |0 S7 G. Q& e# _* R( J7 W- # mysql 锁
' u9 U2 D1 X; M, y" j6 @ - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
5 ~7 F' I. l; o# f9 d4 Y5 t - $rs = mysqli_query($conn , 'SELECT id FROM a');
% {* L$ @* x! J! M9 f/ u - $id = mysqli_result($rs, 0, 0);
( w3 Z+ p4 j% }" @$ k3 A - if($id > 0) - n9 W8 D2 f9 m" x* ~
- {
8 ~" a8 n7 {5 M% j; K5 i# l- A; o - --$id;
0 G* p& k8 r$ y0 O4 U- L - mysqli_query($conn , 'UPDATE a SET id='.$id); * ~% @9 X& G" w$ F
- } : ~& y$ M0 s& c/ ?( G$ A
- + n0 O$ n3 T" j; u* ^
- # mysql 解锁
0 m# d9 ^9 z5 X - mysqli_query($conn , 'UNLOCK TABLES'); y+ Y F' T' d- Y
- //查询解锁后的id值
; o/ `# }- ]. D* W+ k) V: g - // $res = mysqli_query($conn , 'SELECT id FROM ta');! W/ t8 n0 c: p: S8 p
- // while($row = mysqli_fetch_assoc($res))* E( f' k( N D0 ?. p# p- Z9 e W
- // {
* z, n# I" @3 u - // $id = $row['id'];
0 q# _& }1 J* y6 H - // }
% p; w9 q$ G L( m- d - // echo $id;
复制代码 , l, W) X* @. p) ^8 E
0 z' E7 O6 u( _% E
. M" W b% g! T) Z
PHP文件锁示例:
- /*9 i: r7 q C/ _; a3 q
- 模拟秒杀活动-- 商品100件+ o& @7 R2 {" g
- CREATE TABLE ta
6 d8 i4 g+ q& F6 G, \2 x- K/ x - (
( d5 y$ n [$ x p2 a - id int comment '模拟100件活动商品的数量'3 d; {/ y7 H1 T0 t1 F9 j7 A1 B
- );
' C% r0 r; W9 A. [, Y - INSERT INTO ta VALUES(100);" U$ T* E, R, K: S4 E, V$ u1 w
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
8 d+ ]+ D0 d3 l- z8 a - */
/ w6 ^* i3 q# L - , Q/ `6 X {8 ^+ Y9 E
- // 关闭错误报告2 Z& Q- O* Y" F$ o: |9 M
- error_reporting(0);
5 u5 @' v3 _+ b0 l6 Z/ V - ( {1 W" \1 M& m7 @8 }; d0 E- p$ C
- $dbhost = 'localhost:3306'; // mysql服务器主机地址0 M9 Y# F" f& J" N5 D1 @
- $dbuser = 'root'; // mysql用户名, ?' U! M2 ^2 v( y
- $dbpass = 'root'; // mysql用户名密码/ `5 ?! C' O& I0 R6 m" r2 {$ y
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
0 D0 ?# X$ E. `' H! {( d - if(! $conn )6 w2 L/ k% ?; h& U# W! D
- {
8 E; N+ S4 G, H$ X - die('连接失败: ' . mysqli_error($conn));: @4 m H) H* I" J! ]- q2 B' \5 P
- }/ K0 t7 } Q- d- }0 {* P- M ]
- // 设置编码,防止中文乱码$ m3 @4 _" t5 l! u
- mysqli_query($conn , "set names utf8");
5 t, R. V* u0 a6 \" H% X3 f1 C, G6 F - mysqli_select_db( $conn, 'temp' ); * E3 }+ o% K' X6 `. Y7 j# a
- 7 O, |4 y$ j* z
- # php中的文件锁
: H- _6 a( \4 ` - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 : i1 p9 S1 R; w9 ~5 `
- flock($fp, LOCK_EX);// 排他锁 + O6 _) t6 ?0 `( r1 _, s) e
, Z& }" L2 F0 D2 z- $retval = mysqli_query($conn ,'SELECT id FROM ta');
. F$ d, }. u3 U! J' j9 D - while($row = mysqli_fetch_assoc($retval))
# w2 f0 ^7 A4 n$ m5 |; f - {
0 t& o* B7 T6 Y - $id = $row['id'];" a P# V# a( W
- }8 ~4 H* d7 d0 _ W. L, ~9 R
- $ C S& m* G$ k* c& Y$ S
- if($id > 0) 6 M/ Y0 C; _! l/ U6 x
- { 7 _$ H9 e6 r+ L% R, `! l7 m
- --$id; 3 w( T) b, o1 B) W
- mysqli_query($conn ,'UPDATE ta SET id='.$id); 2 w. k7 F9 l5 T4 Z5 S$ ?3 \
- } # l9 r t/ ?! |* q% c; J/ q
- # php的文件锁,释放锁 4 K% k, K& u& o* m# j% `
- flock($fp, LOCK_UN); % V( D0 d6 V1 }! a& |7 Y& o
- fclose($fp);3 M1 ^. l: T/ Y. g8 A
- # z7 ]7 U/ U- Q! Q8 y* f+ Q
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
: u: q% k5 z8 L- N - // while($row = mysqli_fetch_assoc($res))
" q: N8 Y+ D& M+ u& o - // {
& }+ Q. V& g. {4 l$ u - // $id = $row['id'];
( @5 _+ p( B1 O* Z a - // }7 t1 h6 f0 K, G
- // echo $id;
复制代码 ' T3 B V. q% Q c3 o t
/ C; R; j7 E3 e' L. I8 d& S
抢券活动实例:
- public function envelopeSnatching(){/ B$ k! J H* `6 p
- $lingqu = $_POST['type'];( W; [1 m& j4 J% r* i
- $uid=session('u_id');//用户id% W4 o U+ a& R) w9 p0 d8 V
- if(!$uid){- t" |0 b6 A' ?9 M% A
- $data['msg']='您没登录,请先登录!';2 `: }6 o: D9 A: d
- }else if(date('Y-m-d') != '2017-12-12'){
9 S5 V; h; B# N/ L( o6 ?1 K' r% f$ ?2 R - $data['msg']='不在活动时间内!';
* L8 J9 O% U6 P) w7 t! w% Z - }else{
# Y1 v8 H( L8 N% W( a' t) P - $hours=date('H');//当前小时数
. u2 S4 i3 N% ^3 y6 @ - if($hours > '09' || $hours > '17'){7 V7 ]; u% m+ V
- 4 J$ O9 h* T; a0 N8 N
- if($lingqu == 1 || $lingqu ==2){//点击10点的
- Z% F0 ]/ @( B - if($lingqu == 1){
: t1 S1 |/ e" I9 `0 V - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
9 u2 @" B% B1 I% J d - if($hours > '09'){
0 x9 M2 `& \# r4 | - $num=mt_rand(25,28);//优惠券金额
% J% y0 u' U0 l" o - $id=1;
- P' A2 E4 o+ z" ~ - }" M8 C2 \" I; T
- }else if($lingqu == 2){& B1 h' i+ {2 A
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();9 Y! l! b, n1 z" I+ h- s4 F: q% L% S
- if($hours > '17'){
4 L% L4 o# @ B" A H - $num=mt_rand(50,55);//优惠券金额
: r, I) F' M f& M4 _2 a - $id=2;9 _0 v0 [! x3 P' D+ R
- }
: x) C+ U2 L# p+ S/ m - }2 }: `* }. M8 t# `% x
- if(!$id){7 }4 r: F* n' q# F, F
- $data['msg']='时间还没到,晚点再来吧。';
/ {+ T/ W. [+ M* |. G9 U ]! D' ^ - }else{
) Q( h( p2 S z* s; ^: m' v( ? - if($is_lingqu){
4 d/ H: A5 W( e& k7 _+ {0 l5 Q - $data['msg']='你已经领取过了,留点给别人吧!';* G1 u# n" A3 L& ` @; i& W
- }else{
) e) l' {% n# W: ]8 y - //锁表
9 p% c( k, W% J0 A2 _ - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');9 S+ F3 @0 U3 \3 L, W. k1 |
- $active=M('active_num')->where(array('id'=>$id))->find();8 H# F8 E7 ?- K; u: k% K
- if($active > 0){( {: s) ?- P/ ^
- //开启事务3 s& A& [: t% R
- M()->execute('start transaction');
+ T# T, u: c$ n - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));7 \ `; @' H% }+ F+ x6 d% T
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));, d0 Z! \% r! A% ~& D+ p
- $members_preferential = M('members_preferential');0 O* X2 ]" \8 X( s: t
- //对应投资金额,2 x1 J9 O& e6 H
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
/ r# N: L5 E9 f/ Y" ]( u - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
U2 {1 j- | W9 {# r9 } - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
0 ?) \; A. F! { \3 n ?0 d - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券+ X- ~9 g- ~' e0 h
- if($save && $add && $add2){
+ E4 _0 v) p4 v - //事务提交
# n6 j( T8 W0 f - M()->execute('commit');: e! I" A7 L; D' L
- $data['msg']='恭喜你领取了'.$num.'元优惠券';4 n$ _" _' `. J- F" t
- }else{; \8 C9 Y6 I- N1 Z9 V3 c
- //回滚! ~2 i4 S5 `' v( q! [: }
- M()->execute('rollback');
, Z* A5 Q- T! A4 K3 G: Y" I - $data['msg']='未知错误!';9 j, r" Q5 u0 S A) w& ]; w
- }5 a7 U) H. v) r
- }else{
- o- d. u- s5 z - $data['msg']='红包已领完,你来晚了!';
( r+ D' s1 k6 W- J: x - }/ {! L# }" Q5 G
- M()->execute('UNLOCK TABLES');) g- D. v5 K+ |5 T& e/ P$ k
- }
! G! ]5 x% r6 V6 |1 o - }3 r6 q/ t5 a+ p; K5 Y2 @
- }else{- i9 g2 Q, @7 {
- $data['msg']='非法操作!';7 _7 ~9 v! l0 s7 p' [/ _; p- U
- }
: f* F6 j! _5 l - }else{
3 A4 J4 k- [, P% q& ?& F - $data['msg']='还没有到活动时间,请晚点再来哟!!';/ L% G7 u. D9 o
- }
1 h9 O% r' @$ | - }5 L# J5 s. G# U( a$ B) g
- exit(json_encode($data));
; Q% j# a8 b, J$ Z. [ - }
复制代码
" S1 d6 M& v& e# ^ X& U; c
' P' I5 x. z; \: w0 g
| 欢迎光临 cncml手绘网 (http://www.cncml.com/) |
Powered by Discuz! X3.2 |