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://请求的脚本
复制代码
7 K- R+ b$ I/ E8 V) J, KMysql中的锁语法:
7 E8 I* R2 f; \% j" ~; MLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】) B$ [, I+ r, {/ `* _
UNLOCK TABLES 【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表+ ^& }; L3 Z9 b6 S. \3 ?, ?
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞3 B9 v0 t Q- I; F
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :* t1 S' w* D' ]* @9 A' e& T; |
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。
6 D% O( x) L& o* D3 U测试时,有个文件就行,叫什么名无所谓
总结:
* {; s0 ?: B) [! ~8 s B项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景: s/ E1 I; ~) Z8 }3 P/ x+ v
1. 高并发下单时,减库存量时要加锁
c8 U2 D/ Q" R( n: P# [/ p2. 高并发抢单、抢票时要使用
Mysql锁示例:
- /*
- @7 q) B- R6 ?1 F* f- y1 I - 模拟秒杀活动-- 商品100件+ v# D8 s* S7 g5 n" U B
- CREATE TABLE ta
6 _6 ?3 h) |: J4 A( I8 p - (7 T u/ }+ Y) J) P! _7 F; k/ |
- id int comment '模拟100件活动商品的数量'
! z. s, U( C' G: R9 q3 U* C - );& |9 W1 y* Q& x# I; [ ?9 b. ]9 x
- INSERT INTO ta VALUES(100);: P# f8 o8 }; R1 j0 R
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
( P* u, v6 Y- l; a' K - */
6 J# _: J3 j- ]$ a6 i - , ^+ {- t3 K5 i, `8 a; X
- // 关闭错误报告' v0 g. a9 W/ ~; z% a
- error_reporting(0); ( U, S. a; W5 J& e1 a
% ^* I6 k+ V0 W7 w& G+ R* _- $dbhost = 'localhost:3306'; // mysql服务器主机地址
" }1 X" n3 s$ M7 V8 U - $dbuser = 'root'; // mysql用户名: @ l# o+ p! k
- $dbpass = 'root'; // mysql用户名密码: Z% z7 z3 f. z
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);8 e1 G0 E6 P- z+ ] Z9 u! S
- if(! $conn ): B4 ]/ `& `% l# C" o2 p
- {
. W- t2 V! V1 ` ^, g) S# h* @( ` - die('连接失败: ' . mysqli_error($conn));/ N/ a% n6 n. e/ `
- }
R) Z8 S. Q1 h' L - // 设置编码,防止中文乱码3 O8 X- }/ S# `7 r
- mysqli_query($conn , "set names utf8");7 H3 D. |4 |7 V6 @0 K
- mysqli_select_db( $conn, 'temp' ); 3 p7 f& e3 H! `, N P
8 V' b$ A* t/ ^# K& Y) S) c( z- # mysql 锁 / w7 r) L8 t6 x+ I" b6 U
- mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这
" k0 Q2 n) H0 M" i% l - $rs = mysqli_query($conn , 'SELECT id FROM a'); 5 q9 ^( O* K9 l2 \& {5 l) L+ \
- $id = mysqli_result($rs, 0, 0);
8 l; a; N, d' Z9 _ - if($id > 0) , Z) e1 D6 o* z6 c
- { 7 Y3 _2 y! X, S9 X7 D, A
- --$id; ' P( W R( S/ a6 R- M' H
- mysqli_query($conn , 'UPDATE a SET id='.$id); , `9 A, ^) b* v2 `0 L2 U
- }
5 F* M# E0 |" D8 ~
% |# Z/ T( Z- h- t$ y- # mysql 解锁
/ T7 \! f- u" p U - mysqli_query($conn , 'UNLOCK TABLES');8 d$ w, L0 x! k: f2 R! t- M8 b' p
- //查询解锁后的id值
# C2 a$ S1 k/ ^" a; Y% F, B7 d* T - // $res = mysqli_query($conn , 'SELECT id FROM ta');
( |$ M1 i8 r, ~) {% l- k8 g - // while($row = mysqli_fetch_assoc($res))/ Z0 ?$ q" d6 @3 w3 c- f3 m
- // {, z8 z" j$ O# `* A
- // $id = $row['id'];# B! D- }8 Y6 D. c) c5 S4 F
- // }
- g$ L$ p0 k( d2 ]7 h- ] - // echo $id;
复制代码 - n; a+ P2 j- P9 W& ~$ C |# E% j
6 J3 z ?( u+ V7 r6 m( l
: L; c- s" I; T3 ?' ~* B/ k
PHP文件锁示例:
- /*
$ g L5 \6 [( L9 h- x2 M9 n - 模拟秒杀活动-- 商品100件 O2 t* h8 ^* {7 B5 j7 N- S z# X
- CREATE TABLE ta
! X! P8 G8 M+ o' B* A - (( |, I/ F' X: o, ~( S W3 R/ R
- id int comment '模拟100件活动商品的数量'
% T& N$ R! i& {( T - );/ F6 R) G/ h0 w6 C2 W) f1 M5 N
- INSERT INTO ta VALUES(100);, L% @1 q) G# u( n
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
9 F, d% `4 r* h- G- u1 |: k1 ` - */ 1 S M) t9 [% D+ B& ^7 b. h
- % K6 r, b- n9 D& G [. v+ i) r2 r
- // 关闭错误报告
" p* n1 s: V! g. v - error_reporting(0);
5 Y) W$ |) S) K' x9 V
8 f7 k, L- O5 A9 j5 U- $dbhost = 'localhost:3306'; // mysql服务器主机地址! ]5 X8 j: |" ~# U0 Q$ M
- $dbuser = 'root'; // mysql用户名- i# E+ a9 ?9 X9 D
- $dbpass = 'root'; // mysql用户名密码
% o( s$ d. H2 q* D( I+ J+ C' X - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
! V, D5 V5 K1 n# B - if(! $conn )' C }4 Q; _; g+ H& X
- {8 D* t7 U8 C6 O9 [# ?/ H$ c5 Q, N7 p
- die('连接失败: ' . mysqli_error($conn));
/ T8 V; @- _2 S4 P1 F7 q9 a - }
! |8 k) t3 L' E7 j - // 设置编码,防止中文乱码
- y$ P. ?+ u+ E* \4 Z% D1 o2 a - mysqli_query($conn , "set names utf8");' l& }+ F) c7 q$ q( y! b x1 e
- mysqli_select_db( $conn, 'temp' );
) S( y( f+ N' x, o: d - 4 p$ u2 l* x" B# k+ o# T- D- `9 j
- # php中的文件锁
' ]& v* o: d1 j" e( _ - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
- o2 w: N, X8 a, F% `/ A# ^/ M - flock($fp, LOCK_EX);// 排他锁
/ X) Y! j$ ?/ z7 `2 ]4 S
3 `6 ?# h5 |8 f/ p5 u7 c3 ]- $retval = mysqli_query($conn ,'SELECT id FROM ta'); / I+ Z2 [9 y1 [
- while($row = mysqli_fetch_assoc($retval))4 s N2 o% g% Z, s2 h8 U
- {% k' ]6 e; U' Z. f; w
- $id = $row['id'];
9 I, c$ f& |% k" P7 Y - }, @: r, @7 |$ ]; b% O8 z l
-
4 i- E: O" ]" z* }$ t$ v \3 ^ - if($id > 0)
& z8 G: u; T; T - {
; j! @ y1 Q4 F) u8 j3 p3 e2 U - --$id; ' J1 t' m& W) s K( l( j% @
- mysqli_query($conn ,'UPDATE ta SET id='.$id); & g# V- W4 ^5 V) ^
- }
6 h, n0 t2 E' A" @" f: B - # php的文件锁,释放锁 ! |8 M: K; k) ]- K f
- flock($fp, LOCK_UN);
/ ]. h3 ^. k8 d - fclose($fp);
* T3 K3 z% b b' p3 x4 ~4 K4 J
9 K7 ^& x8 v) o, \" ~- L- Z- // $res = mysqli_query($conn , 'SELECT id FROM ta');
, p o! ]9 A) _# q - // while($row = mysqli_fetch_assoc($res))
1 q/ c, M1 C- k2 W/ ~( } s$ D8 q. E - // {" f5 t3 E( g# T3 ^
- // $id = $row['id'];
8 _ l& ^2 R% i$ b: e - // }
3 Q- r: D+ |( t. D - // echo $id;
复制代码
+ e- k7 t( x7 ?8 o) w6 K" w9 |: Q- M7 ^ Q
抢券活动实例:
- public function envelopeSnatching(){
! t# E( {1 N1 G8 r7 N - $lingqu = $_POST['type'];
1 o! Z( j* |+ ~+ _ - $uid=session('u_id');//用户id8 n L" p2 @ i1 I3 h
- if(!$uid){
! r/ V' W* B9 p; y% {# n - $data['msg']='您没登录,请先登录!';9 h* l: U& A6 M- n% D8 Q
- }else if(date('Y-m-d') != '2017-12-12'){* Y" o7 P: z9 M2 }. \3 F
- $data['msg']='不在活动时间内!';
4 L N! p& E4 \) y - }else{, ?+ r$ [! s, k8 n5 |
- $hours=date('H');//当前小时数/ b; B* {+ O7 V
- if($hours > '09' || $hours > '17'){: U# ^- F9 e/ b; Y) a- W
! |8 f4 G; N/ D' |$ c- if($lingqu == 1 || $lingqu ==2){//点击10点的
( W! J" ~2 k9 m9 c8 j2 W - if($lingqu == 1){: B- m M' b# o# N! e
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
) b$ w7 q3 p! H( I+ V, J - if($hours > '09'){
% P, p {% K$ y( k* S6 s3 d - $num=mt_rand(25,28);//优惠券金额# c/ W6 t: N& ?# Y
- $id=1;
" S# v! W0 o" e - }
' m2 g8 [( R- e - }else if($lingqu == 2){
/ b9 v8 f* I n" z* x. X - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
3 A, t! P& J2 y( l; ]1 l - if($hours > '17'){
% _, ?2 M& J: P) G; {. ~) V - $num=mt_rand(50,55);//优惠券金额
3 u% }3 Q& {! w3 a - $id=2;
6 K& b$ L: m( ~) f% `3 a - }$ b0 Z- }6 {+ L2 B" g4 `
- }
( U' S" ]5 U# C6 N; p8 k0 j" M$ y - if(!$id){8 k- o/ `5 W6 P- }
- $data['msg']='时间还没到,晚点再来吧。';1 U/ B1 f3 B; ^! a
- }else{: [5 ^: U( {! T* G) t9 _- X( T
- if($is_lingqu){* e9 H' H5 v8 ?. k0 P& E- D
- $data['msg']='你已经领取过了,留点给别人吧!';4 j6 Q/ ~: C+ y' {9 z+ f6 y
- }else{
! c# f1 _: F* X+ z9 i1 e0 F U - //锁表
0 R1 v) V) ~0 L2 E5 O K0 B" o - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
( a9 N& X* u" x. k - $active=M('active_num')->where(array('id'=>$id))->find();
?% B2 N8 U& \ X. |5 ]; \2 I - if($active > 0){) P. b* x; B( m1 C
- //开启事务
/ `, \& T# r4 H" [ - M()->execute('start transaction');
& u% a. h, ?' f: X6 k - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));8 T3 `9 S+ g1 g& S
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
; o% ?! n; K0 d, _ - $members_preferential = M('members_preferential');
# J# Y4 d% @" g7 f( P, } - //对应投资金额,$ n# h, M' ]0 D8 Z
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));& z2 H( b n1 X) ?9 V
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');0 z3 Q" o5 i6 }2 U% d3 s+ a( {
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间) s9 h7 `8 ^/ [' C% H) N, I3 L
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券" k( V. N, d2 A' j
- if($save && $add && $add2){
. }$ G& g3 k4 s/ e$ ^9 K - //事务提交
% f# `4 ~" G; }3 d& b" [ m) y - M()->execute('commit');) y9 I$ w- z- h; b* F" G+ v
- $data['msg']='恭喜你领取了'.$num.'元优惠券';0 f! O9 R0 s7 R8 Z3 f
- }else{
( p5 {7 k+ W6 `8 o - //回滚
( S7 P, H# r% h8 i) ` - M()->execute('rollback');& |& V J5 c( T# \( s- s
- $data['msg']='未知错误!';
3 D- y$ ]9 w4 ~ - }
! E6 Q) u9 x9 w3 O" Z% f( j - }else{/ p5 W* p, }4 m6 x. J0 d3 i
- $data['msg']='红包已领完,你来晚了!';
- R' J+ B6 M$ U, ^4 { - }- U- g- Z6 o% E7 ], T
- M()->execute('UNLOCK TABLES');
N& u. I; s2 o; L0 P - }, \7 {, G T3 M' {2 ?
- }6 I5 H% b9 m2 D3 {, U6 Y. {
- }else{
- o: b2 e4 l, n5 c) x, A+ G - $data['msg']='非法操作!';
" w7 o% i# [. V - }3 ~! ?3 o2 }0 d: W
- }else{
( ~* t4 s# e, v; L1 { - $data['msg']='还没有到活动时间,请晚点再来哟!!';6 e* u4 Q* [6 \: h$ T/ L; }+ b
- }
! R' f4 \4 {- v* P - }
7 p$ P3 F. b' l- s9 M - exit(json_encode($data)); b& M8 w" g O! k/ b& n6 u
- }
复制代码
- C: U) [ I4 A/ \0 q+ B: J1 J3 Q* V
| 欢迎光临 cncml手绘网 (http://bbs.cncml.com/) |
Powered by Discuz! X3.2 |