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://请求的脚本
复制代码 + f1 v! F4 U* u' Z5 @& I, Q" o
Mysql中的锁语法:
% D* F$ D& S1 c7 f8 X' |2 bLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】2 R7 J( Q( i9 o7 H0 m4 z
UNLOCK TABLES 【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表, v; v( a- o4 Q. m' j2 Q
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞; M# f9 h5 Z+ a
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :
. v1 x) X6 i& ?1 s: g/ o1 o# o& ~文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
6 U* `( Z% m$ t+ }1 w! t测试时,有个文件就行,叫什么名无所谓
总结:
& P, l* s; H" _8 G项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:
( q3 M4 l; v8 S: _8 x7 Y1. 高并发下单时,减库存量时要加锁+ W1 X2 a( L- k$ ~" h: w [
2. 高并发抢单、抢票时要使用
Mysql锁示例:
- /*
, ^8 m3 D8 o9 h - 模拟秒杀活动-- 商品100件( f, i& P& L$ a" m) e: ~0 o
- CREATE TABLE ta. X* |' ?/ A8 B+ w9 f5 e9 f3 |
- (
3 x2 l9 c* S) z" }+ l7 r - id int comment '模拟100件活动商品的数量'7 v& t: ~. e2 j1 X
- );7 ~, h" C) L" j/ q
- INSERT INTO ta VALUES(100);" s% T' ^ N6 T8 K8 Z- R
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件* e7 z" Q+ E; c# G
- */ V! z% g- g* ]+ M9 M
-
8 Q! z. v8 Q3 ^# c, T, H - // 关闭错误报告
+ P: b3 [2 o8 A5 [5 B# j2 }1 ] - error_reporting(0);
5 T4 r8 t0 j+ g0 s - ! F) R; q3 ?2 p& T
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
+ E- K6 E& o/ J7 s# W - $dbuser = 'root'; // mysql用户名- Z& A/ N, ?$ } f
- $dbpass = 'root'; // mysql用户名密码
+ r n$ I$ W, N5 H - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);9 m; }0 y1 _3 L- T& Q! |7 b/ n
- if(! $conn )' ^- X+ r; z f8 G9 b
- {4 q- G2 U' w6 j" H
- die('连接失败: ' . mysqli_error($conn));
+ F+ C' s. w/ O6 K6 D - }4 s5 T( C$ A" w% z6 M7 K
- // 设置编码,防止中文乱码
) u. k1 W! V! k0 j; O1 K9 a' [ - mysqli_query($conn , "set names utf8");3 }5 r* L) B2 g# i4 t( W9 X$ ]; B/ o2 u
- mysqli_select_db( $conn, 'temp' );
7 S- r4 Y% D$ V4 E0 S
' q, q5 H; ?9 O7 W+ h- # mysql 锁
5 D+ n( X, ^! j& \5 H | - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
. J8 b+ E# {, H0 [ - $rs = mysqli_query($conn , 'SELECT id FROM a');
1 |8 u2 ~; E: t+ R E9 P - $id = mysqli_result($rs, 0, 0);
5 A0 p C, l* f6 u+ C2 u - if($id > 0) ) Z+ B0 a, L1 O
- { % Q1 @- W- X! s$ L8 \) M
- --$id;
- Y3 I& G" E! F$ m" j4 v1 ^/ k - mysqli_query($conn , 'UPDATE a SET id='.$id); 4 t1 |/ F- u9 q* y
- }
, ^! j: T+ @& Y$ L
, S8 q: a' F' ^2 l* I" `- # mysql 解锁 - O: B) a. b/ S1 h f
- mysqli_query($conn , 'UNLOCK TABLES');
( F" M, K# m" I; ?7 M - //查询解锁后的id值9 A* ^+ I- {" Z/ w- z* z9 C
- // $res = mysqli_query($conn , 'SELECT id FROM ta');# D' F$ @- G8 d* o
- // while($row = mysqli_fetch_assoc($res))
1 m- y9 {2 \- m8 A - // {
* A9 \: e. p" `* @7 L2 |( y9 O - // $id = $row['id'];
" ?9 {1 \' e; B8 J - // }
- L1 H7 m- H: @ - // echo $id;
复制代码
" x+ [+ V. H/ ], a. J' |% r2 o# c& U! V% b# m# t- g9 ]
# _3 Y% A9 c' N$ V' y; K) i' A# n! zPHP文件锁示例:
- /*1 F' G, e$ N# a j
- 模拟秒杀活动-- 商品100件$ u! S+ }' P1 X
- CREATE TABLE ta! [- X( q& p( K9 S8 g
- (
# H: f w/ \4 F* R) N - id int comment '模拟100件活动商品的数量'
6 l7 J$ f4 Y0 B) Q8 Y - );
0 b$ U2 e8 l( G% x4 v' ] - INSERT INTO ta VALUES(100);* Y+ `" c! l8 b" O
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件8 m; N; J6 ?/ L- q" f0 P7 }4 `
- */ / [5 W# Z: x5 L$ Q0 \. G
- / s& s% ^1 K4 k
- // 关闭错误报告
1 B0 _- T4 D! q# d - error_reporting(0); |5 ^9 l; a- @, b1 ]
- - L5 \7 \- _; n9 v8 z& E* H
- $dbhost = 'localhost:3306'; // mysql服务器主机地址- l- j. `$ p( F, `9 b) I" @
- $dbuser = 'root'; // mysql用户名
( G3 h6 p* U2 U. b6 i ~ - $dbpass = 'root'; // mysql用户名密码" p0 V i! i1 Q
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
+ N# \5 w7 O) I1 r - if(! $conn )9 u _/ v$ `# y% }8 d4 L
- {
5 i8 p, \9 J$ F6 P! n* | - die('连接失败: ' . mysqli_error($conn));1 @" F0 U. s% j) f+ r2 t
- }
: v% t4 D$ c3 k: ~ - // 设置编码,防止中文乱码7 q. `7 b" J2 |! y/ W5 w
- mysqli_query($conn , "set names utf8");
0 z7 a/ L! {/ f& e( q1 G - mysqli_select_db( $conn, 'temp' );
! F$ k! Y0 `+ v' H, b - j! j; `- M3 {: M- Q
- # php中的文件锁
9 q4 H& ^# b9 O: h ` - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 + x, W9 j3 J3 ?
- flock($fp, LOCK_EX);// 排他锁
+ h* R% C* j( u
8 J3 {" F u+ K& c; M3 K1 l- $retval = mysqli_query($conn ,'SELECT id FROM ta');
) j! w& N. k* h( m! u5 ^6 S - while($row = mysqli_fetch_assoc($retval))8 \0 C, W8 q7 w8 Y
- {. a& |4 X. m4 p- W* f& b* B! R
- $id = $row['id'];
# u2 U$ y7 Q* T& x( f! Q& w" p - }
& O/ z( }9 z/ V. y1 e5 N' G -
9 ^- i) o; f; C0 ]( Q5 r" }" `3 Q - if($id > 0)
; I& v9 E0 ?5 `! N* e* X) t+ l) E - { ) i8 z- q# z. F) [0 M
- --$id;
" T( U( a# b! f5 `$ J3 Z - mysqli_query($conn ,'UPDATE ta SET id='.$id);
; p# s: \" f& ^) P4 i5 S5 D/ n# X$ t/ \ - }
3 H: P6 P+ p0 l& O0 f3 k - # php的文件锁,释放锁 ( r( P0 [2 _6 l
- flock($fp, LOCK_UN);
+ e0 j5 i3 ?1 l2 A! u" L5 R" B; y - fclose($fp);
/ E0 X" B3 V9 b8 k1 B* F
: t7 H. S7 h7 r% T. Y1 T8 G- // $res = mysqli_query($conn , 'SELECT id FROM ta');
' m# ?" e; e3 h# V - // while($row = mysqli_fetch_assoc($res))0 e( t" ^2 M5 q7 |# s3 i" k
- // {$ X, r. t" e" [/ s F7 h" F
- // $id = $row['id'];
0 R$ |# Z: g( w8 _$ p: Y) D ? - // }/ R2 c; l2 D ]1 [6 T8 y6 }
- // echo $id;
复制代码
# s) `5 t% i" G* }. F" e' L0 _! |$ ^" M' N/ Q2 ~! ]
抢券活动实例:
- public function envelopeSnatching(){/ O/ ?( m- H5 U3 C
- $lingqu = $_POST['type'];
$ I1 P4 X. N* M1 l7 u" f" f0 ?7 D - $uid=session('u_id');//用户id+ {( u1 z* I x4 R# H' E5 T1 b
- if(!$uid){
9 x" L, B y+ s - $data['msg']='您没登录,请先登录!';
% g) F) \: ^$ S* w - }else if(date('Y-m-d') != '2017-12-12'){
* l$ F5 t# |5 D$ h, _, k' x c - $data['msg']='不在活动时间内!';
$ k2 D. }0 ]4 J! H& e0 m. h7 E - }else{
! b+ E2 C: Q6 H( \9 X6 Y. B4 z( l- v - $hours=date('H');//当前小时数1 Y1 U$ o% r$ s: O, I; s
- if($hours > '09' || $hours > '17'){. x* s0 F8 `( d
* z) C) |4 O5 l6 M4 |9 M8 w- if($lingqu == 1 || $lingqu ==2){//点击10点的
. U3 }4 ?" j F* j( f - if($lingqu == 1){
! }0 u. ^7 Z* \0 u - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过9 z. Q; @% m: t E
- if($hours > '09'){4 j9 Y2 q" Q! m; m* L# ]$ z
- $num=mt_rand(25,28);//优惠券金额
. J1 V* _" J( @0 C, D - $id=1;
1 \7 F0 d0 m+ V" p& e) l2 c1 s - }/ t7 c/ r; l. {1 O& _3 T
- }else if($lingqu == 2){8 |0 J7 k" Q$ J0 s) @. v7 K/ [) S* w
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
" J' O2 q# ~/ Q) s' i3 w - if($hours > '17'){, F+ z+ y; ? X- s
- $num=mt_rand(50,55);//优惠券金额+ y3 [2 s* l4 z! F/ i5 _
- $id=2;/ u, E. y9 U0 y% z# w
- }% w; k; c( A, l, y, `! q8 \
- }
/ G# d" R. F7 L" K9 [. s - if(!$id){
4 o; L8 p, J( D- Q" J4 x8 k$ C - $data['msg']='时间还没到,晚点再来吧。';
2 f* H( J. s* g' V T1 f" \% G - }else{
9 V# J* C, d8 ^3 s* S - if($is_lingqu){
2 u' m N5 }8 Z - $data['msg']='你已经领取过了,留点给别人吧!';1 o1 C" G# P3 s$ _
- }else{
5 F5 X0 H0 R' S - //锁表
1 D g" X1 F+ K1 f. M% Y - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');1 s% F: D O. b( x1 X' m, p
- $active=M('active_num')->where(array('id'=>$id))->find();
2 j% v0 `/ q# `4 v) _3 b$ B - if($active > 0){
% i$ t7 h( [/ w! a3 Q& j4 K# o - //开启事务
. `) E: F% A- s( ~3 U8 ^$ l - M()->execute('start transaction');7 E. n6 ^: q* d3 M. q- {
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));# P* o- Y% | a6 A
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
( y# s ]2 G9 x c4 z* u - $members_preferential = M('members_preferential');
) o+ k1 V, r+ p: W- A0 f/ d - //对应投资金额,+ x8 b+ s3 [4 d7 B
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));5 z4 ~% I2 _1 u) w* Z6 L6 W; C: G
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');) X$ F v6 y3 r. ~& a# f
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
8 r# m* u; F6 r- l4 r+ ] - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券; p( u* n- l9 A0 e" I& L+ `
- if($save && $add && $add2){. S; p0 H1 x( m# g- w$ S/ A
- //事务提交
) H8 B0 w' z4 W5 L! ~9 T+ V% K$ Q+ y- R - M()->execute('commit');$ ?. |3 B; G0 c, T. `# x
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
$ O2 ]) b5 D7 N) k$ y - }else{
: x, A! K2 s- j3 K' v" {8 d - //回滚9 m+ C2 p: C! r* y b# s
- M()->execute('rollback');6 r- V+ a3 ^! W0 K3 ?
- $data['msg']='未知错误!';$ N' B) O/ Y4 {
- }
& P" s i8 ?& e: J5 W8 ]6 o - }else{+ G; H: @* L: ?( B) S
- $data['msg']='红包已领完,你来晚了!';
9 J! @* l: p5 _ - }
! I U4 A. ^. ~ - M()->execute('UNLOCK TABLES');0 }" P; g% z* K9 C5 ^% ^! A
- }
* R8 k/ C8 p: `; F7 G1 Y7 n8 \- b - }2 b4 E6 g% @2 V7 h6 B/ y4 m( Q$ x
- }else{ m& m. N% G0 L2 Z7 S# ^8 s
- $data['msg']='非法操作!';
( j; J8 f, c4 a5 Y$ i/ p4 Y - }
! h$ B; P4 _6 j0 s( p$ w - }else{
+ m6 C0 E7 u1 e' n/ m - $data['msg']='还没有到活动时间,请晚点再来哟!!';' M) B9 G: `; m, [
- }. ?0 W4 `. Y" D7 S. c( j
- }
7 J5 s1 z* ]% B! k - exit(json_encode($data));& n" L' l1 R4 z
- }
复制代码
6 i- y* v* S3 ?+ j0 E$ t
& ?" e% P4 v8 G2 ]0 x
欢迎光临 cncml手绘网 (http://www.cncml.com/) |
Powered by Discuz! X3.2 |