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 \7 t4 Z* J! p7 o7 I
Mysql中的锁语法:
" M1 T3 |) {+ F! ]1 U/ t1 BLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】" h3 g% C3 Q; [6 |+ h
UNLOCK TABLES 【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
! ?; X/ D, n& s% z! O2 }' r9 e) tWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞
6 r, z( b4 ~$ y注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :
! z; |' ^) B t2 n3 e1 A, i文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
$ W9 K: K- {3 F) A( k测试时,有个文件就行,叫什么名无所谓
总结:2 a; m' i( W! Q, m5 p
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:" d" ]( _7 H7 e# z- n$ o# y
1. 高并发下单时,减库存量时要加锁
8 D9 B4 {1 o3 v) B8 O/ r2. 高并发抢单、抢票时要使用
Mysql锁示例:
- /*: d7 }/ r* K1 J' Y0 F$ {$ h; Y
- 模拟秒杀活动-- 商品100件
& d" N0 X6 P H9 w/ | - CREATE TABLE ta
1 R$ U3 [5 _/ g& r) m) Q) ^ - (/ Q$ K. d% v7 t& H+ |
- id int comment '模拟100件活动商品的数量'4 a5 L( B1 r2 E2 N! Y" J
- );
, Q% k6 I9 r( n - INSERT INTO ta VALUES(100);, Z8 U) k8 f1 [( y& i" e L; d
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
8 ^8 X3 Q% c/ T+ J - */ + J: [8 C2 M: |6 w+ R- Q
-
9 ?1 k+ \: W% v - // 关闭错误报告
' u9 E' \* O3 f1 h9 I# b1 q. I - error_reporting(0);
9 }7 K- d0 k( H* ^/ J N, w6 R - , `5 b* R0 I; _6 z
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
, P- H `# {( B0 l+ H# Q3 F - $dbuser = 'root'; // mysql用户名, h; ~# f6 W( b
- $dbpass = 'root'; // mysql用户名密码
* ?/ }( B# h$ t% U5 y: l - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);7 f7 U( D' c! L( F$ \
- if(! $conn )7 _: K# ]8 M0 @' O
- {
+ ^9 |4 t6 b, T% z9 `0 _% d - die('连接失败: ' . mysqli_error($conn));$ n2 t B, p1 S
- }
8 ^- A; y: M8 y: G ~2 e - // 设置编码,防止中文乱码
0 e% V) O, ?3 H: F, i - mysqli_query($conn , "set names utf8");9 g, w) N6 e' O% A5 w
- mysqli_select_db( $conn, 'temp' ); : M8 c! A; R5 v" o7 w
- ! L. J3 E+ s: k% {$ V# m9 k
- # mysql 锁 0 N( a6 t# e- r
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
( U4 {3 C7 F, l) {5 n - $rs = mysqli_query($conn , 'SELECT id FROM a');
7 m) ?* @7 X9 D" ~2 x% Y* R - $id = mysqli_result($rs, 0, 0); ' F1 Q4 D6 M& `; p- r; x
- if($id > 0) 4 A2 A: |( |- u, Q" I3 ?1 k2 q
- {
" a1 |2 ], t1 w9 W. {7 g - --$id;
) S J( P# f9 }! | - mysqli_query($conn , 'UPDATE a SET id='.$id); 8 o5 V$ \& ~6 {( X/ L
- }
; m/ _3 \% s: n2 O ?1 c - ; a4 J- ]" ?3 ?7 }5 [: \' g
- # mysql 解锁 & A8 s# Q9 @" o# z+ ]
- mysqli_query($conn , 'UNLOCK TABLES');! {* { O4 W% {4 l) l; \
- //查询解锁后的id值
; s( f$ c- q2 J4 L, R# _ P6 ~ - // $res = mysqli_query($conn , 'SELECT id FROM ta');
( r! G) e" E% H! Q6 H - // while($row = mysqli_fetch_assoc($res))6 A/ R0 v: h# Q7 e4 Z, r2 m
- // {
. k1 o* e, a( t& R' I5 x0 o# Q' ? - // $id = $row['id'];
) D! g# T+ V2 s$ @5 S& F# W( h5 n$ {6 V - // }1 x1 Y5 V/ A) }, T6 h( m
- // echo $id;
复制代码
- t1 r" ?9 G- G n5 x8 c
7 n* J8 W( X) `" ~. @# x _6 L* J5 q& v) d
PHP文件锁示例:
- /*. {$ e2 g1 z4 y1 x; r
- 模拟秒杀活动-- 商品100件3 ]) u( E+ D+ ^' D/ {
- CREATE TABLE ta' R' D* ~: @; D: f5 \
- (
( O; \3 u+ l6 `- `0 F8 E, {. v - id int comment '模拟100件活动商品的数量'$ T/ N) p- K' p' a. l: ^
- );5 j3 ?8 i/ ]4 k0 H
- INSERT INTO ta VALUES(100);, ]( p% ~8 _0 N' j/ X
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
) C9 T7 Q) J% i; p( R1 ] - */
. C$ a* V( I$ N - 6 I# P9 F Q6 P- w' ]
- // 关闭错误报告' b" Y0 L; l9 g; i7 r0 l
- error_reporting(0);
; @) P9 ?4 n" i% v, i5 U7 D - ) m# D2 [% f0 e
- $dbhost = 'localhost:3306'; // mysql服务器主机地址- [( f6 _; c/ X7 z. v* I* w* M+ x
- $dbuser = 'root'; // mysql用户名! ~5 h7 D* R1 h. a
- $dbpass = 'root'; // mysql用户名密码
. ?+ W. g: J/ D: n- E0 N( C1 p( W - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
$ d' b. Y: C: o- O; m" S* u6 ? - if(! $conn )
# D" ]2 K+ P0 K9 d - {( u& a/ L/ ?- c5 h$ j. `% o
- die('连接失败: ' . mysqli_error($conn));; |6 S' q& z- o1 ~1 }
- }
1 j7 r1 K7 R' G. k. U i - // 设置编码,防止中文乱码
4 n. j T+ T" @6 a+ z# L - mysqli_query($conn , "set names utf8");- N# S" F6 c) C! O: H2 p
- mysqli_select_db( $conn, 'temp' ); 3 L P) G: j" {: V) F P! i
6 M8 d; d( S$ H' u1 ^- # php中的文件锁
+ Y$ r' X2 ]/ m) U% Z - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 . C5 k+ N8 T1 a9 V$ B5 S+ F/ Y5 J6 H7 |
- flock($fp, LOCK_EX);// 排他锁
/ q8 g# Z3 p& _9 P
$ T8 j$ d8 _# y1 e/ @2 g- $retval = mysqli_query($conn ,'SELECT id FROM ta'); ; L1 h& d' P5 L& w# }- K
- while($row = mysqli_fetch_assoc($retval)); X: O' [+ Q1 ?" |* H
- {
- y3 d C' q% k$ d. N1 a7 d$ K - $id = $row['id'];' g( L- e5 s+ S( {6 A( O
- }# q; G5 p# D) d/ o! d# b) f& S. {) o
-
. X# t1 R! ~3 O5 s5 F. e2 T0 L) y! c - if($id > 0) + ?* }% f0 B, k& V/ V/ d
- {
3 K) u. U. p8 h - --$id; ) ?3 Q' e! f" D0 {) C7 R
- mysqli_query($conn ,'UPDATE ta SET id='.$id); ( p- i, `* }( l8 M
- } + e$ n5 L8 L( l
- # php的文件锁,释放锁 ' a+ z" p2 ^/ |5 V2 ~$ c- {$ G
- flock($fp, LOCK_UN);
# u J+ ]) w/ p' C - fclose($fp);
! V: h) Y% I" n6 v5 i
) z z" @+ p }) S; I- g/ D- // $res = mysqli_query($conn , 'SELECT id FROM ta');
; q( p; |7 ?, _2 Y1 m - // while($row = mysqli_fetch_assoc($res)): ~7 v4 b6 S* M3 q4 h. r+ S
- // {
% a8 T( s! z/ D7 @- y: w% g - // $id = $row['id'];
/ u% s" J% _* d5 L$ Q( y5 y - // }! \$ y5 p7 h8 b) ~4 v7 R% f' O
- // echo $id;
复制代码 n/ I- P) C9 M" t8 p0 c- `$ w
9 y& U8 |2 t) {& x
抢券活动实例:
- public function envelopeSnatching(){0 X2 ~, w; y" b5 D, [ P! ?
- $lingqu = $_POST['type'];
3 Q& O3 M2 ]' s. g4 [ - $uid=session('u_id');//用户id+ Q. X! @, O1 k8 ]1 Y
- if(!$uid){* l: g" n1 _. V# }2 d( C
- $data['msg']='您没登录,请先登录!';
+ `' q4 F1 j* J+ c$ d - }else if(date('Y-m-d') != '2017-12-12'){6 r+ y7 Y8 w6 A7 M7 A; \
- $data['msg']='不在活动时间内!';- X- n" T% H+ P( Y6 G5 z
- }else{/ ~0 E, t% P# m( ?" [1 e
- $hours=date('H');//当前小时数
/ Q% @ {4 L" m1 Z* @ - if($hours > '09' || $hours > '17'){
/ n/ U, r* |4 N' ?* u3 ? - 0 {3 K* A K! G% [3 R; L0 x+ E
- if($lingqu == 1 || $lingqu ==2){//点击10点的
! |6 A6 n) J* @/ r5 F - if($lingqu == 1){
: N6 F# J6 h+ z' n& v - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过: g& x" B, a) q1 Y1 R- |+ g( N4 y
- if($hours > '09'){
2 ?# s. h. w1 `; O0 ~& d1 ? - $num=mt_rand(25,28);//优惠券金额- Q( F5 b# }6 Q8 v! D2 I. d
- $id=1;
3 Y/ K" u' v9 e0 u8 Y5 t2 L - }
6 J+ J: I5 k9 o# P+ s - }else if($lingqu == 2){# J) b. h+ x/ }; X9 M" w
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
) c0 H" `' i3 E. B: l1 c; m - if($hours > '17'){
% F2 P% I2 W7 F% X$ V" U - $num=mt_rand(50,55);//优惠券金额
: ?" O: D6 B+ h% c, z7 y( J - $id=2;
' P) h4 S! W' ]( I - }8 I D6 J3 m- u4 d- Z
- }8 |3 \' I& Z3 h5 J
- if(!$id){
# M0 c4 X6 `2 f/ j' V1 H4 d. n - $data['msg']='时间还没到,晚点再来吧。';/ [$ Q) Z; ~' v9 A2 G
- }else{
8 R8 M3 h, v% U0 ?, u, c P* ~ - if($is_lingqu){
3 `( X8 n! L6 l, ~0 i$ t, c - $data['msg']='你已经领取过了,留点给别人吧!';
& }, |1 c. f0 v( K - }else{
. w! j. f4 c# P. R - //锁表; Q; W4 E, I. I3 a7 z8 N% X) A
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');% `# o3 @) _9 U9 z( E4 n' {- {
- $active=M('active_num')->where(array('id'=>$id))->find();
k( q! M6 B7 ]7 V4 n' i" F1 h. i6 ` - if($active > 0){
* O( r* U' h- a - //开启事务) ^6 _4 X. b% j% h+ y
- M()->execute('start transaction');9 N6 x3 I- n3 B4 X7 ^
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
5 X6 `, M! B) i1 S4 @3 C6 c2 J6 A2 F - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
8 Z5 K8 Z: ^8 b6 A( q - $members_preferential = M('members_preferential');
v2 a* h P2 H2 u - //对应投资金额," P( [- q9 d% O5 a
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
9 e; D) q7 \$ O( ~# X - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
3 p9 j# q( L4 _3 m - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间! ~, w- P- g+ p% \2 j; o" O( A
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券" r6 O2 N0 C% d% Y7 y6 ?& h/ W
- if($save && $add && $add2){3 [- y4 o' ?% n7 P( Z$ s
- //事务提交$ r( b7 t* `. v- G" c
- M()->execute('commit');( ]% O$ h7 G" f9 k! ^ _% A8 e* t7 [
- $data['msg']='恭喜你领取了'.$num.'元优惠券';9 q" X; p1 P1 K) w2 s1 \( l
- }else{
' m5 e. V8 D4 d0 ]# W! u9 d - //回滚; H6 Z) N& A, P# Y3 h* W
- M()->execute('rollback');; r/ a0 X" y- k2 y0 W) }& K, Z6 p
- $data['msg']='未知错误!';+ M y( `4 Y0 `8 e9 ]! _
- }( @4 }5 q7 M0 ?4 y1 `5 \; a5 o& B
- }else{
; m ?( E3 D! u7 G - $data['msg']='红包已领完,你来晚了!';
" f4 h8 I; B' T. |- e& n; a - }
- d; `$ F$ r# \: h( n - M()->execute('UNLOCK TABLES');
0 o7 s" Y6 @+ _% D/ z - }
" c" Q8 x: ^# E( A - }
( A4 e* o5 I) x+ K% } - }else{
; [4 b/ M, ~* h0 ^+ ?. C - $data['msg']='非法操作!';9 u3 d- k$ z, a9 y8 |1 l
- }
* n( ]+ C3 |5 ?: v" F" {- F - }else{$ q2 e+ z/ }% E, P! {' k8 E g
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
9 u! ~% I. W! T0 l: V - }
1 b' y7 j) L4 ]3 T9 Z, @7 Z5 k - }1 e( s, O* i& |+ w' ^
- exit(json_encode($data));
4 m; \# `( r' n8 Z* E. ~ - }
复制代码
- J4 N# C7 I; m# E3 z; E6 y3 ^2 E! z. g9 T& K3 E, I5 M! f
| 欢迎光临 cncml手绘网 (http://bbs.cncml.com/) |
Powered by Discuz! X3.2 |