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://请求的脚本
复制代码
+ J' @7 m; t) C: F5 I" C3 t7 ~8 R! YMysql中的锁语法:/ d: P' {$ T: \) v
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
. [& T" ?' y+ \2 s9 @2 {, [8 rUNLOCK TABLES 【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表5 c+ X* f; U) ^1 L8 |5 p/ N4 C' N7 U
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞6 b p8 i$ C+ ]% U
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :
4 Z/ S8 h; N7 U8 Y! p+ r7 x文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。5 Y" H9 E1 Z* w- V5 S- {# Y
测试时,有个文件就行,叫什么名无所谓
总结:
; J% b' G) D3 W; r, w& o项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:
: \1 o9 b( ~$ M- j7 N4 k# T1. 高并发下单时,减库存量时要加锁4 Z9 T" ^, v6 f8 S* C( k! i
2. 高并发抢单、抢票时要使用
Mysql锁示例:
- /*
, O& b; G" j8 ~ - 模拟秒杀活动-- 商品100件4 E9 s. Q9 e J- R, U4 _
- CREATE TABLE ta! d& M, U# \/ ?0 \
- ( Q$ ~: S8 i- M# q; C
- id int comment '模拟100件活动商品的数量'
1 z7 M7 E0 l: P3 U) Q6 k - );
" ]1 u* v3 K1 D, r& C9 X3 s, D - INSERT INTO ta VALUES(100);; n; j; M( {; n% X' z, d
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件8 R- v' q W5 Z* o) X( G
- */
8 s) d) d/ [1 t$ g! g -
2 Z3 r+ k+ ]: i$ } - // 关闭错误报告; S' f! I' T) d" {' B8 L
- error_reporting(0); & Q4 e* F5 I4 R# A+ l- F2 C. ?
* e, c( R& f' g2 w6 \% L- $dbhost = 'localhost:3306'; // mysql服务器主机地址 p# q W$ p) K) a
- $dbuser = 'root'; // mysql用户名. i' S) Z0 [( v& L+ F- G" O
- $dbpass = 'root'; // mysql用户名密码
, W6 G4 B( g( E5 z+ F! U - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);! ?: P$ f( B* u: D
- if(! $conn )# n- L: x; `3 P" _7 l4 n& N
- {1 V4 g7 e o$ C# v
- die('连接失败: ' . mysqli_error($conn));! c# r' a, }* D; |+ [* P1 `; ^
- }# [- I) e; q% L# S8 x
- // 设置编码,防止中文乱码7 A6 S& B+ `" {4 j) T
- mysqli_query($conn , "set names utf8");- U+ ~; k# e, l' g) i" o
- mysqli_select_db( $conn, 'temp' ); * }2 E2 H$ U' Q. i
- / Y2 b4 R" H5 I
- # mysql 锁
' F O; |) Z; U3 U2 }3 v - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 0 I; {3 a' p2 P3 d
- $rs = mysqli_query($conn , 'SELECT id FROM a'); 7 k8 J" [4 x+ |6 h% K$ j. ~
- $id = mysqli_result($rs, 0, 0); 6 a# `, @- L; s4 z
- if($id > 0)
) P3 g/ N9 \$ M/ A$ ` - {
2 Z3 n' b( O# W2 F, r - --$id;
' K" T5 w' i W3 \ - mysqli_query($conn , 'UPDATE a SET id='.$id); % X, j- E9 @- ~+ f
- }
9 j3 Z, }# }, N3 D3 x - ; z$ `- G0 h+ V: U( W; k! }3 C* Z
- # mysql 解锁 ! W( A4 O c1 w0 i6 u
- mysqli_query($conn , 'UNLOCK TABLES');/ {5 Z* U+ B6 L4 D5 Q! _8 g1 u/ n
- //查询解锁后的id值
, |; l/ L, b2 x7 I% P0 E - // $res = mysqli_query($conn , 'SELECT id FROM ta');
3 ^/ R: v5 F$ N& y+ P2 D2 x8 K - // while($row = mysqli_fetch_assoc($res)), _6 z$ o- [8 w2 h% f
- // {4 v; i& ?, u, w" S, |, T: R% @7 Y+ u% g
- // $id = $row['id'];( X# I% F' g; `* `) U/ q: L
- // }9 t- t$ `5 L0 ]3 H' ~1 Z- E
- // echo $id;
复制代码 " ?6 x7 x: ~6 V/ k
) R5 c. L$ M; K( u5 [0 y! v& r
( @$ e1 v2 r0 T' z1 i
PHP文件锁示例:
- /*6 L+ T, _$ \6 b
- 模拟秒杀活动-- 商品100件
7 F$ ?& a' e; I+ p6 |7 X- q7 k - CREATE TABLE ta
) b0 |* z+ B: u; u: B% P/ { - (
" u/ u7 F/ i- N9 }' B- G - id int comment '模拟100件活动商品的数量'
7 |9 [ `' g$ g7 w# [, L - );
% D8 l% I3 C+ B$ ` - INSERT INTO ta VALUES(100);
) b, y- \" r7 A( q$ f W# U" l - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
7 x) x0 G. _/ D+ z - */
1 u2 m, O$ Y( b4 r" Q - ! s& i6 P; p6 A) x4 Q
- // 关闭错误报告
) ?; A. z! J6 d: X s, m6 S - error_reporting(0);
6 g* ]+ }) F5 C9 s7 G+ }. ^ - 7 d8 y8 ^+ V$ m9 J; }2 y% x) M0 z6 q
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
! S8 W- O, P# v5 f1 j9 `- ?2 c/ H - $dbuser = 'root'; // mysql用户名
) M3 W' }9 y( }% M c4 Y* }! C. O - $dbpass = 'root'; // mysql用户名密码3 Q* f l# S* t# m4 A) L" P
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);' o* ~. P4 ^% d4 |! G( p
- if(! $conn ): O+ v' v: P2 U$ S6 _, S' p5 M) O
- {- U+ E2 G( u) m( B: Q5 z( t3 U4 S
- die('连接失败: ' . mysqli_error($conn));8 q3 Y2 W6 C% l! a; O1 U" \
- }' I( @+ _; ~" A, P( f2 O
- // 设置编码,防止中文乱码4 q4 h( t; I1 l4 m j
- mysqli_query($conn , "set names utf8");
( p, q1 f& ~0 Q2 V u - mysqli_select_db( $conn, 'temp' );
( i9 [) T$ H D8 E) Z4 }
& @9 E C. q; l4 V- # php中的文件锁
1 h# z) Q% g1 T! {; t2 _% j' r - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
! t4 T" g1 ^/ K- F0 ?% f- e6 N - flock($fp, LOCK_EX);// 排他锁
0 w% t1 R* f2 _% w3 T# O - 1 A2 Z0 H* @; C3 H! B9 N
- $retval = mysqli_query($conn ,'SELECT id FROM ta');
. S% F8 O' A* z' p5 J - while($row = mysqli_fetch_assoc($retval))- a* J4 Y6 ?2 X
- {6 ?' |0 C7 g# G
- $id = $row['id'];
) ] G# @$ G+ o8 Y$ t" Z+ i - }
! W9 C8 [. f V+ Q; _. H- s5 d - ' B" _9 }& p' s; S/ J6 U
- if($id > 0) , F( {) a; y4 X
- {
2 `8 }" n. l9 E; n0 N! O% `% J* B - --$id;
( h% @3 S% F- I8 @ - mysqli_query($conn ,'UPDATE ta SET id='.$id); - A: G4 v. Q+ V4 C* S
- }
: v8 R6 F6 Q! y, p# P; d - # php的文件锁,释放锁 ) U/ [5 w% B; G( t: J- S7 M
- flock($fp, LOCK_UN);
6 O, ~, v" G( R- f7 c1 k0 f7 d - fclose($fp);3 ~* q" b* }: {! X' C/ j" B' d8 s, l' O
& W4 |4 w& C& G2 K2 G- // $res = mysqli_query($conn , 'SELECT id FROM ta');& @& {- @8 _) Z0 p) t
- // while($row = mysqli_fetch_assoc($res))
o# G9 E9 z% ^4 ]: T9 I- r$ j - // {' {! q3 p+ b; v& S8 H2 R8 K
- // $id = $row['id'];
( l; f0 B- l: J. \; N/ {, j - // }
7 ~$ f' F$ C% |9 r/ g - // echo $id;
复制代码 3 G1 p$ I( `2 v# F- Q( c
! | t" A! X p( k
抢券活动实例:
- public function envelopeSnatching(){: e- D& ~' u3 Q/ D* s9 {& c
- $lingqu = $_POST['type'];
: C# ~- c, Q# g$ z! S& G" L' Y1 b - $uid=session('u_id');//用户id+ j5 h* ?) _! n8 z+ v) ?1 X
- if(!$uid){6 ]% ?! }( C" _7 i
- $data['msg']='您没登录,请先登录!';
/ {) j( g. d6 \* M - }else if(date('Y-m-d') != '2017-12-12'){
, c* I: B I2 t6 a) Y; M - $data['msg']='不在活动时间内!';
& V* @( v) T( K" ]) B4 O - }else{' T6 F% P, V* L
- $hours=date('H');//当前小时数 r8 b/ @% ]5 J7 L, r0 e
- if($hours > '09' || $hours > '17'){" u: {6 s4 ~. c' O
; L. W- [- d' V# O5 c- if($lingqu == 1 || $lingqu ==2){//点击10点的
9 n. {9 z, g4 Z( k2 }/ b - if($lingqu == 1){
( f( I. p9 K$ {* U6 t7 L - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过2 |6 b: ~! V3 h# t8 R/ y5 U) c
- if($hours > '09'){
: b% c+ }) j3 @4 [4 \2 z; i - $num=mt_rand(25,28);//优惠券金额
+ R* u# j; W. j - $id=1;" S% j% W: j- b0 Y( V
- }
! ~9 Y3 X7 K; g - }else if($lingqu == 2){. M, w1 l; m% e
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();4 M! z$ [8 D) l( Z Z9 {+ d
- if($hours > '17'){
( F* o' }; p& N; w5 L- ^ - $num=mt_rand(50,55);//优惠券金额/ d0 P2 H9 r! x! {
- $id=2;
( d B. l7 Z, m$ i - }
* `6 ^2 `5 v/ X% W( { - }
! s' J4 S5 H/ ?# _ - if(!$id){
" s3 S X: I A% D+ o - $data['msg']='时间还没到,晚点再来吧。';' j, t: l( y6 ^" I
- }else{8 r2 ]- @ X1 {) B$ s0 q) v
- if($is_lingqu){; w; C1 A! `+ D
- $data['msg']='你已经领取过了,留点给别人吧!';: {3 S' I, @- C+ N! d3 w! g& |/ I
- }else{
9 K: Z4 Y a: J0 ?/ t4 J1 r - //锁表
0 O4 L, ^. ]. }/ e o4 o$ I - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');& z8 a [0 Z9 T9 r
- $active=M('active_num')->where(array('id'=>$id))->find();8 I Y- o9 Y5 v+ t) @1 K1 R, e% r
- if($active > 0){9 ]6 P1 e9 v. {3 s
- //开启事务# Q* `9 K; h* s0 m5 H2 ?
- M()->execute('start transaction');
# L: d0 k. P8 l, u' G: y: Z - $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
# A3 J B# l3 d% L8 P - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));8 B* G8 m1 F" t# l3 R% ?, } i+ J
- $members_preferential = M('members_preferential');
6 ]3 ]) [6 h; f, M - //对应投资金额,4 |5 b% E) [' ]: R/ [2 @4 E" [
- $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
' P a8 T- k" |& f$ J Y2 | - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
* h& B7 S, R( X1 @2 D - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间
" Z5 x# y( r/ _5 m% r, G& P- t - $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
/ w& J/ I9 D4 S1 z# _4 P - if($save && $add && $add2){; k; j) ^: g% u' n R1 J
- //事务提交
$ E4 Y- f" s; { - M()->execute('commit');2 G, C# w2 V! R8 i! N
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
1 A3 D, z' p% s3 W - }else{+ e& w3 R' L$ P. V5 [% K$ m1 ~
- //回滚
% b2 b5 O: u* i& @: D - M()->execute('rollback');
+ v/ U6 O, }! T" Z8 X - $data['msg']='未知错误!';, ]! N2 d, t# u
- }. a# `; e* {$ e+ L. A
- }else{
: N0 a" c% T& B5 ] - $data['msg']='红包已领完,你来晚了!';# t _. C! n( B4 [4 }( ~; \) n+ Y
- }3 J- s5 ?. a; p0 C
- M()->execute('UNLOCK TABLES');
) n. T8 `, a4 W& a1 F' O. W# i9 _ - }8 g R% ]# B4 }- M) c4 {! G4 K
- }7 k% J4 A+ L1 ^1 Z8 O: x6 R0 n
- }else{* S3 R' j4 A3 n$ l! H4 |9 V+ F
- $data['msg']='非法操作!';
& ~1 O2 E5 n2 [( f6 e; ?; D - }
9 E, }; K# Q! R$ Z - }else{
! J; U2 B! x: k C) D, ~ - $data['msg']='还没有到活动时间,请晚点再来哟!!';4 n/ f. T; y& f+ y
- }
/ e( Y$ p5 U! R% T - }
% p Q I* B' z - exit(json_encode($data));
! h6 }& N7 B3 @! ]' Y& G9 r - }
复制代码 ?/ `' L0 N6 `
; P1 L& B+ G- y. i2 \, i$ U
| 欢迎光临 cncml手绘网 (http://www.cncml.com/) |
Powered by Discuz! X3.2 |