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://请求的脚本
复制代码 % [- B. L% V8 L( B6 d& S3 ?
Mysql中的锁语法:
$ U4 k' S# J/ A$ s' w4 y" m# c0 H4 |LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】6 `6 ^: O' b/ M; M
UNLOCK TABLES 【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
9 L/ C- G: b2 U6 x$ @6 ?8 hWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
) y+ L1 G E& l3 j3 m注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :
" r' M% ?' Y3 I! Z0 u4 x文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。2 c `$ [% `8 k4 F% _, l
测试时,有个文件就行,叫什么名无所谓
总结:, O) j! T% t! N' I( R3 M4 B
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:; P& A3 k6 i$ q: L4 Q4 C4 ~: F
1. 高并发下单时,减库存量时要加锁! ?6 d" e7 s6 J* P0 I) }0 q
2. 高并发抢单、抢票时要使用
Mysql锁示例:
- /*6 j3 j. P, C) O6 {7 ^5 I+ n4 Y7 x
- 模拟秒杀活动-- 商品100件: R) C; n7 i& I6 Q2 m ]5 B
- CREATE TABLE ta
' {6 X( L5 V: e! R- ?( ]4 v - (
5 `$ s% Y0 X2 Y6 C! c S - id int comment '模拟100件活动商品的数量'
( B2 I4 A) d* t: n& t* x - );
: _- J/ a/ R* e6 P - INSERT INTO ta VALUES(100);; P4 P8 l. P D
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
; q3 a( l. T9 V- y9 X* m - */
, |5 n4 @: k8 N4 |! w- \ -
3 z0 H6 J) j8 @3 A/ G - // 关闭错误报告& a/ Z5 e# Z) \. `
- error_reporting(0);
6 p* Z: F0 o; c" T6 b; ]
6 Q( T% l9 Q# A# @- $dbhost = 'localhost:3306'; // mysql服务器主机地址
/ u: v3 e3 o3 k; V - $dbuser = 'root'; // mysql用户名
?/ b9 ?% \, ^3 L" K% X$ O - $dbpass = 'root'; // mysql用户名密码1 W: t$ u/ @0 X) _6 d; L: N
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);) J& Y& E$ b& g5 q$ W0 r
- if(! $conn )1 F. C1 y! y6 J* {/ D" T
- {
7 F/ z% L' s, ^3 @* z4 p - die('连接失败: ' . mysqli_error($conn));
4 H5 h! [: x j. i" x& w. X - }- J- a: [8 C( [) f# g8 m
- // 设置编码,防止中文乱码
# {9 t/ w; H& c6 m# l; B - mysqli_query($conn , "set names utf8");
9 g$ P; n& n0 h1 }! Q - mysqli_select_db( $conn, 'temp' );
# q+ {& R$ p( l& }8 u s8 ?3 |8 [ - 5 X) F$ L' Z7 z- C8 H9 i) {; J
- # mysql 锁 0 |0 l' G. y( K& w2 D/ Q! P
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
8 ]* Y0 I( P0 f/ ~3 M$ c) k9 j9 M& z - $rs = mysqli_query($conn , 'SELECT id FROM a'); 1 @/ K5 `- k+ \% I* c
- $id = mysqli_result($rs, 0, 0);
0 K$ z9 d: s4 E1 `* \ - if($id > 0)
6 F& j1 ]6 l& {2 e- K0 m- y1 C - {
5 ^0 A, U* ^: k - --$id; ; j4 J/ X1 t! b: Z0 Y d7 N7 c
- mysqli_query($conn , 'UPDATE a SET id='.$id);
/ P3 @% _8 ]4 m% c5 j9 O - }
0 c4 _2 k- P3 X: r, I, s
1 @0 {! n5 k3 ^- # mysql 解锁
) P6 ?# C8 m+ \/ z! q - mysqli_query($conn , 'UNLOCK TABLES');
! f6 x( f2 i& @2 {- Z7 A) c - //查询解锁后的id值
7 J% _, D& n N8 y4 I0 T - // $res = mysqli_query($conn , 'SELECT id FROM ta');
2 T5 P. M+ F6 A6 Q* s4 t - // while($row = mysqli_fetch_assoc($res))
; J q! N7 v6 B2 F! L* q/ H - // {/ P9 G( A* F5 X
- // $id = $row['id'];
0 c+ p7 P% Q* {2 |5 p( t - // }
! r, a3 ?9 [. ]( |, O# T% K - // echo $id;
复制代码 2 e4 |. S" E4 _& R2 X! ^7 ]
" b2 Q; ~8 h: Y, v" m- Z+ H; h5 P* i7 @* G4 W9 y+ A1 C
PHP文件锁示例:
- /*4 n3 L$ ?7 y* Q$ t9 P, q% ?& ^
- 模拟秒杀活动-- 商品100件. o0 _6 _8 I9 {1 Q' B
- CREATE TABLE ta0 h! F5 ]3 G, ]) s9 b6 t
- (4 H% h5 t. v% n+ k0 i) k
- id int comment '模拟100件活动商品的数量'
h: j0 C* O* A - );
8 F$ u; N* K, O* Q0 k, m - INSERT INTO ta VALUES(100);4 r; ~8 _+ D7 p
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件7 {4 q' I, c. t1 k! D
- */
6 i1 V% ?' M8 X% U -
; ^/ ]) Z: n5 s* ~2 m - // 关闭错误报告
+ {: x+ U( Q" f: D - error_reporting(0);
0 o% A" |+ ]8 J6 T; @* o - ! g9 |1 X" P% Y! N8 Z* y: r, a
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
7 X- x& r' U, `& j5 n - $dbuser = 'root'; // mysql用户名
3 S% \. D$ s6 k' B+ @' U - $dbpass = 'root'; // mysql用户名密码
8 R S9 g, {" d - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);9 B {' o5 D9 b, L5 R7 {7 v
- if(! $conn )+ Q3 e( {- H; g6 E- |0 \) n( g7 h
- {
# X$ Y8 e' R- M. V/ k - die('连接失败: ' . mysqli_error($conn));
+ N: S9 m8 w' Z8 R3 K - }
' |! a9 ]% z, e/ b - // 设置编码,防止中文乱码% f- J0 M; ]& e2 n! |- ^! d
- mysqli_query($conn , "set names utf8");
4 O$ u& K- v3 P! r1 s - mysqli_select_db( $conn, 'temp' ); ( c( C& I- I, U% z6 i+ L6 \
" P! x0 u" A3 f7 d9 a; G- # php中的文件锁 8 u+ e+ B. W. T$ J
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
. w* k% m( F) t* b - flock($fp, LOCK_EX);// 排他锁
# M A7 Z2 F# d1 k* |
2 L; S& x5 i T- u# Y n. [- $retval = mysqli_query($conn ,'SELECT id FROM ta');
: A9 t* F! z' x7 I$ m" G8 { - while($row = mysqli_fetch_assoc($retval))' }4 g* ?/ \, q' b2 U$ n
- {
+ z1 V. ?" l7 s5 d8 j, f' E - $id = $row['id'];7 ^2 ^( U, Z, U0 Y2 Y! _' R" B
- }
& o" v& g5 d% ] J" t8 L1 @ - 6 t9 t- K4 y% ~' y$ j7 [; d
- if($id > 0) ' N! y/ m, i6 O8 B
- {
/ [! G, [# Y0 v" S' C& O - --$id; + R( ?: n# ?+ ~$ ~9 f- X7 D
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
% W" X* N' Y- o, e, } - } z' ?1 X/ u$ v, p6 j5 _0 h3 p
- # php的文件锁,释放锁
. ? Q; |& x# n q - flock($fp, LOCK_UN);
- j* U6 R6 C9 a5 ] - fclose($fp);
# }1 |, @8 k9 s# r - 2 U( K* U$ @5 g8 T8 d; {4 i0 L
- // $res = mysqli_query($conn , 'SELECT id FROM ta');! c4 m ]! n3 S! _6 s ~; N: v' t
- // while($row = mysqli_fetch_assoc($res)) W; Q" `. [& e( V \
- // {- f4 e: d5 x# W w! l
- // $id = $row['id'];/ p+ h0 N1 h5 _& h w
- // }
0 Z: h( u9 Y& D1 D2 B - // echo $id;
复制代码 ' ~& }4 k: F) E0 A: ~9 ~4 w1 t
" e; v9 l: w: U5 N% |
抢券活动实例:
- public function envelopeSnatching(){
2 G2 j. V, u' z. W - $lingqu = $_POST['type'];
7 o2 I* y$ J: g0 [/ A - $uid=session('u_id');//用户id
& R/ k2 D0 u3 ], u7 {; F- C! s. k+ u - if(!$uid){
/ s+ r$ x8 a1 }" n# i - $data['msg']='您没登录,请先登录!';
# N% B9 d4 V. Q" Q/ w# r. m: ?% S - }else if(date('Y-m-d') != '2017-12-12'){
% c v& {% i) F - $data['msg']='不在活动时间内!';* N8 j0 ]& x. n0 @3 v- b$ H' u
- }else{
- X5 t8 e1 W1 [8 w% i! |- M% }. e9 _ - $hours=date('H');//当前小时数3 M+ O4 O* M0 j+ A
- if($hours > '09' || $hours > '17'){
9 m% S# s5 j" [, i" y! S' m - ! U! A" G4 t7 m- |" t
- if($lingqu == 1 || $lingqu ==2){//点击10点的
$ j" [/ b" n9 p! e- C! B - if($lingqu == 1){( H- \7 d! R, r
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
* K- K2 X5 L* F6 V0 \( H - if($hours > '09'){0 @5 C0 I6 |9 y+ v: B' O
- $num=mt_rand(25,28);//优惠券金额0 G$ |- l5 n7 L0 I3 ~
- $id=1;6 @" c0 n" m/ y/ S1 E2 C9 Y
- }5 J( s$ P9 |# S* k
- }else if($lingqu == 2){
; I. N% O" f4 Q: y/ H' Y - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();# } [; u% o' Y. h8 o u
- if($hours > '17'){
8 R3 x# s8 x( q# b1 U - $num=mt_rand(50,55);//优惠券金额$ P( K w9 o7 T! ?" B
- $id=2;2 t2 A! h7 H8 h U* a$ i
- }9 t: Z- V) h; d! S# m1 }
- }
% x' d9 e$ \2 P0 G$ a# Z) m1 H6 E - if(!$id){
* }6 ~) Q1 y5 ^1 u$ V9 |- Y. a - $data['msg']='时间还没到,晚点再来吧。';' r7 l& }9 r1 ^5 Y
- }else{3 D* M2 x8 l1 k) N. ?
- if($is_lingqu){. y6 O) A7 k! \4 l3 ?
- $data['msg']='你已经领取过了,留点给别人吧!';
; o- r [& b+ K# ]1 q - }else{
+ H6 o5 s) A( K+ O' d* N# T2 _ - //锁表7 n0 [- E( T8 ~' @/ S$ x
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');$ V7 e S5 |6 X2 k1 }# O
- $active=M('active_num')->where(array('id'=>$id))->find();
& k/ u; I! g; w/ V- y - if($active > 0){# i0 R0 Z1 M3 \) {" s- X
- //开启事务
2 h' g/ Z+ q& ? - M()->execute('start transaction');0 _" a( N* \2 \. N" Z
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));/ |) Y6 Z# r4 G% \* G" q8 N
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
- i5 `; k$ a' b6 ~% F - $members_preferential = M('members_preferential');
: v+ D7 i( P5 c5 u - //对应投资金额,( j% u( q% ]) h1 b
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
6 u9 E6 L- F+ L1 t v* D - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
, R" q' Q4 M' e, A2 Y+ ? - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
/ {; s9 i7 l3 [* u& V5 I% ] - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券" s) Q! _ ]# h. O0 G7 _5 A7 L
- if($save && $add && $add2){4 u" W: e/ m4 Q Z
- //事务提交
8 F3 p# _8 R) _$ |8 p& N - M()->execute('commit');
/ |, i# h! L0 b- | - $data['msg']='恭喜你领取了'.$num.'元优惠券';# S9 [8 L8 \ `8 q8 K7 e
- }else{
" b2 y S" r% O- A$ a - //回滚) D- P( P1 U& e
- M()->execute('rollback');
; L$ g% J6 M' ^0 a5 d! {, c - $data['msg']='未知错误!';0 R$ q! l. ^( U) }+ k
- }
7 x2 \4 a9 L/ s1 m6 |; z% u - }else{8 T: i$ v) L% B1 j3 Z0 V% H+ G
- $data['msg']='红包已领完,你来晚了!';% l9 M, Q. M6 X5 [4 `1 U
- }+ L9 ^' R8 M* z* E
- M()->execute('UNLOCK TABLES'); }( q/ X/ k/ E. A
- }
; k2 H' [( z2 a - }
9 O. t; a5 g& e5 i - }else{
( B1 `: m7 [5 |$ l$ D0 w - $data['msg']='非法操作!';# I) t$ P4 d0 N9 @9 r
- }( z5 C$ e: @0 Z4 g
- }else{
' p, C0 [, a3 ?" D( G Q - $data['msg']='还没有到活动时间,请晚点再来哟!!';9 |) W: b( Q5 e# C
- }
" Z7 L: O+ l' T+ ~2 ? - }- E1 b4 \4 r9 y$ ]" E2 T- Y7 m0 U N
- exit(json_encode($data));' E0 f2 c/ {( c* [7 o' q
- }
复制代码
2 s! x+ ?- z! {" p3 U" l) ~7 x7 X" I
: I3 J X$ G& |# i9 l! { o; w
| 欢迎光临 cncml手绘网 (http://www.cncml.com/) |
Powered by Discuz! X3.2 |