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://请求的脚本
复制代码
8 [3 j* P4 J' i& y: K( |2 \2 O. HMysql中的锁语法:. w8 U) J& Z& t" o8 c$ P, Z; U- \
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
$ j% D' f1 A9 Z' m8 |9 HUNLOCK TABLES 【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
6 z: c, ]' j# m0 T$ Z: q; l% vWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
: R: j5 P0 x- |( H& C! b7 ?注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :' |! N' z7 }% K; L+ [
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。 N4 t; Z7 M! ^
测试时,有个文件就行,叫什么名无所谓
总结:
0 Z' T* I7 b$ J. O8 `# j项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:/ C' k# H# M) }" U7 n! [
1. 高并发下单时,减库存量时要加锁
0 i- }* z: M! m5 E6 J; j- u$ ~2. 高并发抢单、抢票时要使用
Mysql锁示例:
- /*% @' E) n9 X2 ^" o$ h! A; X* ?
- 模拟秒杀活动-- 商品100件7 n4 U. W! }% K: k8 l9 T9 P4 C
- CREATE TABLE ta% }: u1 n4 F" s d3 J
- (! H' z; d& N6 @* s* ?: h3 y! F& T
- id int comment '模拟100件活动商品的数量'
4 l9 p& a2 q# x2 i( z6 j% k9 Y! _ - );
' ?, t& d6 j. z$ I. a - INSERT INTO ta VALUES(100);
8 G+ s* J7 ?% Y% U1 N% d - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
) v7 ^8 L! K+ O: i1 P1 } - */
4 i; N: `6 C" x- ^' Q5 N3 ] - ) c0 B2 O0 H1 d& P* N, ~. i
- // 关闭错误报告
5 U5 k, _6 l5 \% H - error_reporting(0); $ X) f z0 t- U, x: i
% B& X; m$ k, v* u3 L- $dbhost = 'localhost:3306'; // mysql服务器主机地址
* e( f9 s; t* S - $dbuser = 'root'; // mysql用户名) P2 |1 v9 L0 k3 f. P4 ~
- $dbpass = 'root'; // mysql用户名密码
) n% s+ H: Y" d: _* D1 E - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
; a. b1 v8 g$ w. n+ P3 w7 K ~1 N - if(! $conn )) Z2 C# z4 e, _& Q1 @
- {- ~9 b1 x, S9 F* g% o
- die('连接失败: ' . mysqli_error($conn));) ?. m' N% y$ }& B( o
- }
/ [4 p' w& F# f( O( i, ]" P1 u - // 设置编码,防止中文乱码2 ~8 p1 t# M+ p" p. \# |
- mysqli_query($conn , "set names utf8"); a# c, X& I3 {4 ]6 {
- mysqli_select_db( $conn, 'temp' );
7 Q8 q( ]) ]+ I S" P, T, B. C - ) \( y* p( x6 {
- # mysql 锁
& B8 E5 J# O. f$ i - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
( s7 {5 s( V' E7 m3 }* i$ ? - $rs = mysqli_query($conn , 'SELECT id FROM a'); ; s- A- f) F+ l1 s+ M2 @4 ?+ B
- $id = mysqli_result($rs, 0, 0); / O: ?( l$ _7 v, A" ?3 p9 i
- if($id > 0) # P! I( P& q6 V$ }8 J! R5 a" ?
- {
" v b, S" v% }. S) Q - --$id; " \% D! [2 _6 ]6 V4 J+ I! j8 U+ B
- mysqli_query($conn , 'UPDATE a SET id='.$id); " V9 N/ P6 `% i) N
- }
2 Y2 c. M& O6 u. k6 b; P - - A: M* Z& g: A$ |" I
- # mysql 解锁
+ E7 y& `' |1 } - mysqli_query($conn , 'UNLOCK TABLES');' l3 n5 R3 B7 w7 A0 l& ? [
- //查询解锁后的id值
* q2 {$ A& Z& S3 T P - // $res = mysqli_query($conn , 'SELECT id FROM ta');6 W1 x- p4 i8 i1 |. g) x' j
- // while($row = mysqli_fetch_assoc($res))
, w# o$ Y" G( m5 B5 Y7 ~ - // {
+ }2 V- \- \$ @( C+ y - // $id = $row['id'];
6 f$ C) [4 _! w% H; a; o2 u4 } - // }5 f: Q4 q" z$ W, @3 a7 i8 r
- // echo $id;
复制代码
5 N/ ^9 u& [% H W0 Z1 Z5 K/ N; {/ r1 u) f, \* H
0 W- d! C# H! c! TPHP文件锁示例:
- /*
$ x8 F+ E- c. ^" [* g) ~; U - 模拟秒杀活动-- 商品100件
0 X( p$ w( Q: M3 ~/ X3 M5 F- i - CREATE TABLE ta
0 X+ I3 H5 v& x) ?4 i- l3 H - (7 ]$ Q* `: k- K) F9 r5 J# c5 o2 t
- id int comment '模拟100件活动商品的数量' W$ f0 V" @- w0 s# P: F8 q
- );
. b5 \2 r( k6 x! J) l! H - INSERT INTO ta VALUES(100);8 J! S- k0 b, E9 K$ H
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件( u i" u6 a# O) G( K0 m
- */
% }% O" Z( [- a) h6 s7 K; a - / e& l+ u* L& [3 ]" n% d% i5 ?
- // 关闭错误报告- h# j3 p0 y b3 c" n9 N& k/ g7 \
- error_reporting(0); : e2 R: i( s6 l4 V( P; I
" y7 S: v1 h8 J* N: \' e- $dbhost = 'localhost:3306'; // mysql服务器主机地址
9 d: X5 d- ~/ W; x2 m - $dbuser = 'root'; // mysql用户名1 m+ }* r& J$ B
- $dbpass = 'root'; // mysql用户名密码
2 ~. Q3 ]$ h! C9 R - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);( K9 s$ m4 X! {7 l. \4 v/ w# P
- if(! $conn )
$ ^$ K3 _2 }+ a8 ?4 Z7 l - {
7 a Z% j8 f+ U8 I - die('连接失败: ' . mysqli_error($conn));
% ?3 l0 w' J+ \+ t& ^( d% c: y. E - }3 \' V& G0 k G% Z' ~5 ?$ I+ c8 @
- // 设置编码,防止中文乱码( W6 X" B9 b7 F/ |! a
- mysqli_query($conn , "set names utf8");
- d5 O7 Q( x+ s# x - mysqli_select_db( $conn, 'temp' ); 9 }6 n6 O9 Z' `1 I4 p( }: Q, Z
7 ?+ Y* c1 @( r- # php中的文件锁 / |- C/ M: w" d. v* N
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 8 O% {: Q: k' V* c
- flock($fp, LOCK_EX);// 排他锁
" @, T, ^3 `* A
1 H, N7 o h8 j4 F+ W, r! y* d4 K" a- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 1 _3 h5 c2 Y3 Q Z
- while($row = mysqli_fetch_assoc($retval))9 r }8 J# G0 S
- {/ m+ a7 j, X. s. X
- $id = $row['id'];) B( ]8 x+ |6 B
- }, u0 {0 U7 ]7 |: S
-
. x: H5 ?1 T% D! L! p5 E - if($id > 0) ' P" q* u/ b8 t A0 P! C4 O t: u
- {
8 L5 |- j+ {8 d4 [ - --$id; 9 p4 \1 ?" J1 v1 \3 r
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
: B% k! u$ B X# G* Q& x% Z - } 6 r+ P% F8 }% r+ S( }3 i% J; r6 N0 U4 G
- # php的文件锁,释放锁 ' M+ c* {5 Q) q/ N
- flock($fp, LOCK_UN);
. E6 V* s2 b; v - fclose($fp);
, L* [, ?7 N% a# O
; w3 k5 P" _! N* U! X- // $res = mysqli_query($conn , 'SELECT id FROM ta');
' T$ m" l7 g6 I# C' ] - // while($row = mysqli_fetch_assoc($res)) g& X4 a) w* U" }/ r: A
- // {
! [; {- h! j) ?8 q; G; L - // $id = $row['id'];
' \* n# W6 G1 x: w# M( V - // }2 t2 h U, a( [# L* E
- // echo $id;
复制代码 5 {$ P2 k0 l/ _) u* M: c$ O
) q1 b, C6 _6 c; d. N; Y4 R, @9 B
抢券活动实例:
- public function envelopeSnatching(){
" j- @# T) B6 G$ A# ^2 ` - $lingqu = $_POST['type'];
% o6 U0 {4 e& }3 k9 o - $uid=session('u_id');//用户id1 O2 |' @3 v' @/ s
- if(!$uid){
' c- [: X2 f! k9 G: s! e$ Y- F7 o( Z - $data['msg']='您没登录,请先登录!';
; F- I% d/ M+ K# J, T) j - }else if(date('Y-m-d') != '2017-12-12'){
6 W5 z3 t& Y6 v2 _% x7 A6 K - $data['msg']='不在活动时间内!';6 l+ t6 _6 x+ D: i" N7 g, F
- }else{% I. h9 Q) p( C: e+ R
- $hours=date('H');//当前小时数' k% @1 J+ L1 c4 ]) C# y! m
- if($hours > '09' || $hours > '17'){, R% g) E; R8 L
- ) G5 v9 U% D7 b9 o# d/ g& m, Y
- if($lingqu == 1 || $lingqu ==2){//点击10点的; Q, ?/ m" N- ^: z
- if($lingqu == 1){
& F* ~# T+ o" B$ z1 I - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
4 H I- z1 v7 M - if($hours > '09'){
- J4 i/ E; z0 m2 H5 K" U2 O7 K8 B - $num=mt_rand(25,28);//优惠券金额3 h' p6 Z, D% D5 a- j
- $id=1;( M' l5 c t2 p
- }: |- r0 W# s! N l6 r' x; k! M
- }else if($lingqu == 2){
. c6 T% m" d, A- d8 P; }( O - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
4 A; [: t' A$ l; v( N9 I - if($hours > '17'){8 u' c* ]+ D \" u9 w- W
- $num=mt_rand(50,55);//优惠券金额
0 p) H0 K* U7 J" Y8 C/ t - $id=2;, J& H' P1 @5 N2 _/ F
- }6 p) L% }4 ^7 g8 d9 D, w
- }. n! m3 g6 l! W, o
- if(!$id){$ \7 C% k9 N- |: {( P* f8 W
- $data['msg']='时间还没到,晚点再来吧。';/ T5 m* |1 u0 t0 \& l
- }else{
7 h! C! J. F5 r/ u# H8 x - if($is_lingqu){8 b7 C' P4 I) Q) h( w
- $data['msg']='你已经领取过了,留点给别人吧!';) {9 |* E6 Q0 }5 i# `# Y/ u
- }else{ G7 M) q4 Q. w) u$ F
- //锁表
# h* N% Y' o0 { - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
: [, [+ o- A9 r+ _& |4 g2 N' X - $active=M('active_num')->where(array('id'=>$id))->find();3 f7 `; c1 t3 ~. r1 i! }5 l. Z. Z: d
- if($active > 0){' m' _; G( d- r* [( C. u
- //开启事务" W3 O# v# P% M& W6 m/ L* g+ ?
- M()->execute('start transaction');
) d0 K' C! _9 S9 X! y - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
& {7 W) v! A% I7 E; e. w' S - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
6 I4 x$ L5 e+ a1 G - $members_preferential = M('members_preferential');
( y$ t7 `( g' M* v# {# l - //对应投资金额,
1 c; }# g) _) w/ }( G' b+ O8 U - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));! W3 U4 p. U0 f% l2 M
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');9 a; I6 O& g+ A; a
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
& ^/ P, T3 c. ~& Q - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
: Q& ~% i G5 n1 ]& P - if($save && $add && $add2){5 L) p4 w* P0 ~5 {
- //事务提交 K& j' |- V% m w: v# ~5 j6 c' P
- M()->execute('commit');8 W- [- Y) n' }3 c% }7 }& H( v$ g
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
4 j* U' z0 D) w( E) m - }else{
$ |# U& u7 z. p8 C; J/ h - //回滚
3 }( E' d# v! j% n! M/ y1 ? - M()->execute('rollback');
1 ~: _& x' R; x1 ?% [" n - $data['msg']='未知错误!';( l9 s! r7 u. z$ C! {) V% P
- }( J6 W% Y& ?) F
- }else{
" o% M; r5 ^* ^) ?0 Y. r; D/ y - $data['msg']='红包已领完,你来晚了!';
! P; ^1 u! H* `& ?* j- t - }
" C9 D4 u: S5 i8 z, B; k* P4 X - M()->execute('UNLOCK TABLES');
- B7 r' S# n+ x9 _: o - }4 {( ?* ]% r8 H o% n6 [3 A0 `
- }; V# A0 W) ]! g V
- }else{. M" s$ z0 u" m9 ^( g+ N1 D
- $data['msg']='非法操作!';4 `" K9 ?$ z6 E; [0 D
- }$ c8 y" h9 p$ s! K* i/ \7 i
- }else{
& n0 m+ w3 M5 F! T1 h - $data['msg']='还没有到活动时间,请晚点再来哟!!';& s3 C( K7 g1 I7 i3 ^0 I6 F) f
- }
. n+ c ]" @7 }* w+ P - }
6 V6 O- {# w& l( t8 Y! J - exit(json_encode($data));4 o' p, T# x- d9 N V( X, Y- U
- }
复制代码 8 N* X f1 B! G
! O$ {& T6 t0 |/ \6 X5 H' F
| 欢迎光临 cncml手绘网 (http://bbs.cncml.com/) |
Powered by Discuz! X3.2 |