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://请求的脚本
复制代码 * \2 x4 i# C: }# O/ W R$ F
Mysql中的锁语法:$ z' E" a4 Q ~$ r, a: |6 T/ k
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】4 X- u" M, C4 D V* D0 k
UNLOCK TABLES 【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表; ]# g4 Y+ o3 s3 {2 \5 M: s
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞& \, V3 I: ~" I% w9 c* Y
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :
7 _) w: n. L; S7 p7 g- j) u7 O9 y6 k* \$ G% s文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
4 x5 \- \" _. \ O测试时,有个文件就行,叫什么名无所谓
总结:
$ K/ \( S' u! X& y项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:
& L6 M, w V4 j$ S$ X/ n1. 高并发下单时,减库存量时要加锁
- p( j+ [+ A" o6 a- [6 C2. 高并发抢单、抢票时要使用
Mysql锁示例:
- /*
# `; d; d9 p" q/ h: x. @ u" x - 模拟秒杀活动-- 商品100件* @, Q. y- C$ K) ]0 n5 d
- CREATE TABLE ta0 e* F2 x% O( g8 D* y5 a
- (4 t& I3 b( J1 M9 _$ ^) \
- id int comment '模拟100件活动商品的数量'! H, U+ K$ T- {' t; F7 e
- );4 |, H! V8 r6 r3 [( v! k5 {
- INSERT INTO ta VALUES(100);" f% r6 O- a; w0 I" |6 O( i
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件$ J& a. Q6 V- r8 }
- */
7 l# H4 U( e. t5 w# W. U& o - % {$ L$ o7 y2 ?( d$ b1 k
- // 关闭错误报告( D5 {* O4 G0 Z6 n# i7 Z
- error_reporting(0); 4 k4 k- `* J. T2 F
- : ]2 \# _ s5 M' z
- $dbhost = 'localhost:3306'; // mysql服务器主机地址- W* [! H! g7 W' u
- $dbuser = 'root'; // mysql用户名
) P, S; I+ H- f. i/ c - $dbpass = 'root'; // mysql用户名密码 q8 } Q% @9 v
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);: e* G) y8 Q3 S5 A! Y% n9 y' I
- if(! $conn )
, O7 ^. P$ \0 u# r - {$ `% p- z' z& d- z3 d
- die('连接失败: ' . mysqli_error($conn));
0 C" p* ` M! a9 k9 |" t2 `# Z, \0 s - }
9 e: n8 r) |6 x - // 设置编码,防止中文乱码
5 l' h$ s# Z3 C# ` `* d - mysqli_query($conn , "set names utf8");" N, s e6 V. g- h
- mysqli_select_db( $conn, 'temp' );
& R% P# o8 c- o& ^9 k$ Z+ |
% R* w# ~' q; L9 X- q- # mysql 锁
" b2 P s+ i0 X5 T: h3 c) e' d7 p* R - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
u7 w6 c7 T1 I, \ - $rs = mysqli_query($conn , 'SELECT id FROM a'); * J% I0 c1 p7 f0 O: m# \
- $id = mysqli_result($rs, 0, 0); 1 X+ }$ L' W5 i
- if($id > 0) ( Z1 d5 G) f$ d* m
- {
$ I. u& W6 b2 z+ W - --$id; % u C& m# ^- t
- mysqli_query($conn , 'UPDATE a SET id='.$id);
3 V; X4 y5 M* e& [ - } 6 s6 C7 D$ |0 `, c/ c$ V5 ^- u
- 2 o3 x b/ v1 u% \; ^, o
- # mysql 解锁 ( H) u" H7 Y; {0 {5 o9 `. I
- mysqli_query($conn , 'UNLOCK TABLES');6 g% c1 `$ m2 I" R3 e, W! X
- //查询解锁后的id值# f) L, ]6 S y- j
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
' s9 [( E. p1 j$ q2 t) t - // while($row = mysqli_fetch_assoc($res))
! R @" N' h5 I( b: b/ y - // {
2 y0 W) W$ s9 P1 b5 u' N8 m - // $id = $row['id'];7 Q6 }( J: y: J+ Z- ^2 y% ^
- // }. K! E3 J' H, _
- // echo $id;
复制代码 ( i0 p9 q, V$ w0 L
5 p& w4 \/ t: Z; |! m$ Q) Z' v
+ t* O1 |) |$ D9 M$ R: f
PHP文件锁示例:
- /*4 e b$ T( w3 t! {0 U+ k, x
- 模拟秒杀活动-- 商品100件
5 } A9 N3 i, I$ y5 h# N - CREATE TABLE ta
/ |9 r( \; |- c1 ^4 C7 x+ C - (% t$ Q/ t, v1 ~! B' {
- id int comment '模拟100件活动商品的数量'" b9 {" G1 u7 z* x
- );" A" i t' X( C( v4 t7 F7 m
- INSERT INTO ta VALUES(100);% p& Y. U: o- C2 `6 E8 Q4 b
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件3 ?0 x) U6 P$ O, G* D5 F
- */
7 b4 @$ m4 \! b* r' K. I7 n - ; ?9 t0 D4 C8 `5 b* R$ R/ l7 y* k
- // 关闭错误报告
% c, O* B6 q' \) s7 K - error_reporting(0); , ^5 m- K; [) E0 w
% g$ P; `: {7 L: c- $dbhost = 'localhost:3306'; // mysql服务器主机地址% q+ A2 j$ v/ R* O6 v0 H
- $dbuser = 'root'; // mysql用户名
, s2 L; U3 _, L/ L - $dbpass = 'root'; // mysql用户名密码
1 x! J6 n& P" P) A4 Z4 C# U - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
8 n w3 |4 G& @ - if(! $conn )9 V% n/ K7 }+ r% a
- {% v% `, p8 Y+ N
- die('连接失败: ' . mysqli_error($conn));; o. p9 p% o ~3 j1 o \
- }$ {- I) t% q I
- // 设置编码,防止中文乱码
; C; S1 ]3 ^4 u3 j" \ \ - mysqli_query($conn , "set names utf8");* e2 F% Y" z1 d9 w) R
- mysqli_select_db( $conn, 'temp' );
4 q2 M% D7 p* i. ] - 2 A, w2 H+ E) Q5 p$ ^: K
- # php中的文件锁
' a: B7 `8 c1 X& k8 a7 W - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 - V) \& H1 E: i3 s1 H" U& Y
- flock($fp, LOCK_EX);// 排他锁 * T9 Y: N* }; X( \& G" F2 h
- ) W. s' `6 I r( c; S8 R
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
, _- K# \0 ?1 M! s" [1 M - while($row = mysqli_fetch_assoc($retval))1 n8 ^+ {) `2 h
- {
- G' G. j; e! a# \# W& V W - $id = $row['id'];
2 i, v! D: l3 \ |' ?$ {! r - }5 `0 ?( _; J+ p# d6 a0 s" o+ [0 C, V
-
( G# N! Y f' \1 H7 r - if($id > 0)
; j4 a* `- C" k" }2 L0 r - { & Y' Q, s! P' m8 S V% G* }
- --$id; $ a0 C, a) f# p f1 Y
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
- W# Q& C2 e/ C) d0 A+ | - }
3 f3 a$ h0 X$ W& y N - # php的文件锁,释放锁
* V( O& ^5 e( ~" Y - flock($fp, LOCK_UN);
/ T* z7 M, }4 A' k - fclose($fp);+ D3 [: ]/ r1 `$ L3 [- J$ q! n; p$ @/ h
* z1 v3 x+ x6 w5 P9 a E# x- // $res = mysqli_query($conn , 'SELECT id FROM ta');
1 g% Q: L" A8 S' x* N; L) g - // while($row = mysqli_fetch_assoc($res))
; ~2 l7 J& Z( P% M0 M" O - // {
% i& U O- n% c. q0 F* ` - // $id = $row['id'];
+ L6 k2 M+ K) Z, w4 i: y - // }
4 d+ J, ?% H# ^% g3 \. ? - // echo $id;
复制代码
7 W' W" y$ Q3 M( ]
9 K0 U1 b" `8 o抢券活动实例:
- public function envelopeSnatching(){) |0 ?: |+ s" N3 j! x" f
- $lingqu = $_POST['type'];
+ Y3 v) m; A& q v5 X+ L - $uid=session('u_id');//用户id
$ y5 X# y& _+ B0 s - if(!$uid){
% @$ _; K6 c. S v - $data['msg']='您没登录,请先登录!';. Y1 m1 I; i6 p: B" E, X
- }else if(date('Y-m-d') != '2017-12-12'){
- g4 g8 z! \- [ - $data['msg']='不在活动时间内!';
+ V! N" G8 ^2 @4 n2 X, T - }else{' A# d3 @$ B F) o$ P; e3 H
- $hours=date('H');//当前小时数6 P9 H1 \6 G- N5 U/ w% N& ?2 W: t3 L
- if($hours > '09' || $hours > '17'){6 m9 ]9 x5 y7 V7 k9 c8 a3 q
# I& M5 s! y3 r" L$ r: Q8 ?- ]- if($lingqu == 1 || $lingqu ==2){//点击10点的: U& K# x5 Z9 C% i" E
- if($lingqu == 1){
0 O; b- |# Z+ E* u x: I- m! P - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过0 D/ J" L! l2 f/ s/ j6 {' L
- if($hours > '09'){' X8 |6 h8 m7 h! \
- $num=mt_rand(25,28);//优惠券金额, [$ g" k) t+ l1 |) Y1 ]
- $id=1;! g, N' e1 x5 }" {( k' \2 L. ~
- }- l: \' \- N0 B3 ]
- }else if($lingqu == 2){
" X8 }, z. V& L1 j4 c7 R0 V - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
3 q* L* C& B$ q9 E - if($hours > '17'){
, E% p9 O4 |) @/ P9 L- g4 q4 o! |8 ]9 M$ P7 R - $num=mt_rand(50,55);//优惠券金额
3 V: u8 Y! N; ^6 M9 L0 Q! m - $id=2;- _% I9 C" f' W# M- H
- }* d& B8 z1 L( r- {" f
- }
1 T4 ^! |1 V: g, y* y5 K& V6 K' R - if(!$id){
- `" ? e# r: m4 x4 `5 U( [ - $data['msg']='时间还没到,晚点再来吧。';: n6 c7 U6 a' `2 `1 V& U# g( B4 \* ?
- }else{7 \8 @% K" O. k P
- if($is_lingqu){
- {/ E Y6 w' h" ^4 p/ w F - $data['msg']='你已经领取过了,留点给别人吧!';
4 x8 w2 {4 I; E* B7 Y: D R8 z. ? - }else{
3 t% \9 h( f, M2 l9 T% q' p - //锁表" s9 D8 g, B! }7 l- I N+ Q1 p0 ~
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');0 W: D3 v' P$ B: m7 C& ?* }+ T
- $active=M('active_num')->where(array('id'=>$id))->find();
' M6 w2 l# q; {* a - if($active > 0){
) G$ ~" a' g- @ - //开启事务: e7 v, s9 Z( |" d$ Q4 Q' ]5 C
- M()->execute('start transaction');( s* {2 E' F3 f! }1 S p! D- }3 K% E
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
) I2 W( ?+ u# B* Z9 I9 T$ } - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));3 a6 x7 O8 P% @
- $members_preferential = M('members_preferential');
8 U$ \7 S2 A& p0 r8 w - //对应投资金额, D* g3 W: K- a4 z* V- I! W4 @
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
/ j5 Q$ `- q9 E/ `7 W+ r$ h - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');# _4 T5 V' [! z, q; D8 H. L
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间: d% b8 `5 f$ O4 f3 o
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
& _4 C" G4 ^" e5 I0 W - if($save && $add && $add2){ g2 {6 ], Y- d, D/ o; R: D3 G
- //事务提交
$ n6 a9 r5 I; A - M()->execute('commit');
: A& B' h6 {+ n' D$ [( t& D- I0 G - $data['msg']='恭喜你领取了'.$num.'元优惠券';$ U0 C3 ^2 ^+ d3 U5 N+ `* e
- }else{
$ v3 f, A5 W: n. D - //回滚* N: W' f8 C& P# z
- M()->execute('rollback');; u- q1 ~7 g7 K' t$ W' P& e* W m
- $data['msg']='未知错误!';
) R' ^9 C. f* u3 c5 h* U' c - }* w" w( @3 P1 K1 k; r4 z
- }else{
& i& S: o# x9 }+ E% d - $data['msg']='红包已领完,你来晚了!';+ h$ O! _; g- j
- }8 ]! P1 I% W8 n+ _0 M( v# K
- M()->execute('UNLOCK TABLES');, j' c8 V D/ j6 T4 G/ ^
- }5 z- P0 q1 u0 G$ D
- }- M4 ~: W2 ~$ w6 k( |! Z5 R
- }else{1 s4 H2 j) `. P% m
- $data['msg']='非法操作!';
/ V8 f6 h* W% E9 B - }; K5 @9 J9 L" d0 N
- }else{
# H. \& i8 F1 t% |; d3 T. l - $data['msg']='还没有到活动时间,请晚点再来哟!!';
2 X+ j- K0 ]$ s7 E9 C - }, M( K6 ?& J, N
- }
; n( j& c! ^ m' D D; _4 |6 S - exit(json_encode($data));0 g8 y/ k! y! ]9 o
- }
复制代码
* h. K4 p% d6 k$ R( ~" O$ }/ U: u2 N0 _2 P# H0 r- E$ H9 q1 }
| 欢迎光临 cncml手绘网 (http://www.cncml.com/) |
Powered by Discuz! X3.2 |