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://请求的脚本
复制代码
. C. F3 p, b/ A5 I" V; q/ SMysql中的锁语法:
: Q: ^4 d8 M" T( T1 BLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
9 z9 \1 u; H! U- _/ N3 H- c& B6 vUNLOCK TABLES 【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表- `9 g( u) V7 k5 s4 {. n- q
Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞; v6 E7 K, P* }, T
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :+ l$ T+ x/ g4 ]- P
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。4 E* F( o% r K7 q- P& m
测试时,有个文件就行,叫什么名无所谓
总结:
% K2 ~3 x1 V# H# @. G项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:- n4 h: g z% q: R |, G
1. 高并发下单时,减库存量时要加锁7 u: H e5 p( a9 q
2. 高并发抢单、抢票时要使用
Mysql锁示例:
- /*
% T; I: [0 S! W/ ~/ i - 模拟秒杀活动-- 商品100件
7 _0 H. M8 A2 i( B9 R - CREATE TABLE ta- `0 M8 o/ ], c' T( {
- (* N1 q1 B8 i9 b
- id int comment '模拟100件活动商品的数量'. A* |& r. n7 x
- );
: ~& t2 \; X3 e0 Q" O8 | - INSERT INTO ta VALUES(100);* {2 l7 m$ p6 B
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件# \- D: O) d2 l. A% e) A
- */ ( E, v4 K' n% x3 h/ \ U2 i$ f
- ! ~9 n% \8 A0 M% l& N) V
- // 关闭错误报告( o& u( {3 ?, d# f1 V9 J9 @ }
- error_reporting(0);
+ T6 r8 e" b5 l, D - z4 A* \5 }3 M9 o$ |: {4 z
- $dbhost = 'localhost:3306'; // mysql服务器主机地址* N+ H. A+ k% o
- $dbuser = 'root'; // mysql用户名. \8 i$ }0 u- G3 V, F% X
- $dbpass = 'root'; // mysql用户名密码
7 @. E" Z7 o6 ?4 z: ~! C; `2 ?# f - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
. R# u: y& V8 F+ a. n& f - if(! $conn )0 R- T3 ~! D1 e# f
- {
& C" ]6 P. v) J, k6 [; y - die('连接失败: ' . mysqli_error($conn));
" v3 H& ^0 r8 v$ m5 Q - }- K+ ~6 ^; S3 U# R; f$ A
- // 设置编码,防止中文乱码+ E8 e0 Y1 S3 E, C6 D( c
- mysqli_query($conn , "set names utf8");
1 C# [8 J( ~$ V: r" b9 u9 M. i - mysqli_select_db( $conn, 'temp' ); & f3 k6 o% V& Z5 w3 B; l4 [1 R( f
- # h- Y. \0 T# X5 P/ Y6 U. Z
- # mysql 锁
- ]# q+ N! x# S, ~# Z7 m& b - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 F1 C+ u3 ]2 `& \0 y- k: X% _6 c3 |
- $rs = mysqli_query($conn , 'SELECT id FROM a'); : o0 G2 S; g. g- }! A6 t) @; l
- $id = mysqli_result($rs, 0, 0); . {/ {* j1 H) n! ]" I9 J7 i
- if($id > 0) 2 P9 m- M. y. w( Z
- { + y8 q# Q a4 F7 X1 ~3 X
- --$id;
4 I, a' D, Q$ O - mysqli_query($conn , 'UPDATE a SET id='.$id); 6 f, c. ~' G' d3 P$ \
- } ' o! |, }% m ?; s/ l% I7 x7 J% S
- ' `! o! j$ B: N9 g
- # mysql 解锁
/ u! t1 n+ J6 h+ A0 O) g - mysqli_query($conn , 'UNLOCK TABLES'); K8 O# y7 e2 r0 `
- //查询解锁后的id值
5 ^, Z" J1 v8 ? O - // $res = mysqli_query($conn , 'SELECT id FROM ta');4 A1 @# |; `7 g9 {" H; k- `+ j
- // while($row = mysqli_fetch_assoc($res))- |% o. U* g7 M( l. v
- // {
* ]0 U% ^& q# R - // $id = $row['id'];
9 C( r+ Q3 m1 c4 h5 H$ S - // }+ s" W2 ^( J6 t1 x) E4 a( z
- // echo $id;
复制代码 ) W% u9 m# g/ I
" y6 `; c( S, M! R5 r" C$ k
3 t5 C0 P6 u5 lPHP文件锁示例:
- /*! m3 r/ @: j7 k
- 模拟秒杀活动-- 商品100件
- R9 f& a: T& N( R: K9 e - CREATE TABLE ta+ }. y5 F" w- Z) s8 q, h: Z7 d) e
- (
& I) e' _8 K) @9 Z( I; T - id int comment '模拟100件活动商品的数量'% F5 J5 O( j& O8 {3 _
- );
; O+ M/ S$ f+ `8 ]: | - INSERT INTO ta VALUES(100);
( o4 I1 w! k. M) m" {$ X - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件4 T- \$ ^3 i) S3 Q$ t
- */ + D1 U6 Z6 v% N1 z! m3 W
-
) q2 Z- K; S% L* @& b& i - // 关闭错误报告
A* R9 T& e8 d8 O; g - error_reporting(0);
0 d9 V9 V* t j
+ x/ b7 y7 }1 u) a* H" ?- $dbhost = 'localhost:3306'; // mysql服务器主机地址
/ e; ^$ H$ `6 ?% y% \8 b1 U4 |' L - $dbuser = 'root'; // mysql用户名
( e) L8 @% {5 p - $dbpass = 'root'; // mysql用户名密码9 T+ T1 v$ T: Q
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
8 v7 D/ N1 s0 f7 x - if(! $conn )
+ o4 p4 ^1 L" x2 o% s4 h+ C - {6 _+ g0 J9 O+ D- B7 J
- die('连接失败: ' . mysqli_error($conn));
: m7 Y7 T8 h2 O3 V/ L6 n - }2 q# Z# y4 m9 k0 M: L( r
- // 设置编码,防止中文乱码, P# `' l1 e+ ~# u" u) ]
- mysqli_query($conn , "set names utf8");; f% x# C" o! r9 W4 h, g, X$ f
- mysqli_select_db( $conn, 'temp' );
7 c+ s# h( Q- U - 0 {5 W, `* [! i' T1 U
- # php中的文件锁 ' z2 O+ ]; _9 U$ G
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 7 D* | J! C2 J: _6 s( ~( i7 B1 Y
- flock($fp, LOCK_EX);// 排他锁 + _/ f7 [* X9 w
- 2 O* e) S0 X1 D0 Y6 |
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); : g1 b3 ]3 m! Y8 m7 n) C
- while($row = mysqli_fetch_assoc($retval))
0 r& v, ^( N3 ?% L - {
! t: n3 \. [) e# J+ b - $id = $row['id'];
6 j6 `5 ~# e6 a" K, d: c7 V - }
6 `9 G3 f& b' b - ( C' o3 P) ] e) t* Q( @2 }% i
- if($id > 0)
" L* ? G9 X8 P7 g# g( d - { 9 s, N( T% M7 A2 U$ ~8 `) w
- --$id;
4 J9 U4 ?3 k X- k3 f - mysqli_query($conn ,'UPDATE ta SET id='.$id);
! \3 `: |# @" y - }
8 o4 w) e9 O& v* U z - # php的文件锁,释放锁 $ y) k, ^: w. h6 ?
- flock($fp, LOCK_UN); - Q7 ]" W; a8 Q% x/ A1 S$ q2 o! X, ?, s
- fclose($fp);5 y8 i" x V8 U% Y
- ( n, F/ Q, U* @ ?
- // $res = mysqli_query($conn , 'SELECT id FROM ta');! j: \+ g. \+ i1 d& \7 ~8 A0 C
- // while($row = mysqli_fetch_assoc($res))
! W! x7 r, R3 \% O - // {, l1 I$ K) m1 ^0 l: N# w
- // $id = $row['id'];) j" s z2 U7 e0 {0 }/ n, J
- // }
. O+ T& }( R7 C% [. \5 } - // echo $id;
复制代码 , @) n3 q+ I+ V/ P X1 q$ _' J
& X M5 p3 e4 a1 c- K: H2 U7 H
抢券活动实例:
- public function envelopeSnatching(){
, L8 Y2 y' G3 S$ R, T - $lingqu = $_POST['type'];
- a: Y$ f. G9 p: w - $uid=session('u_id');//用户id0 `+ ^8 C9 v, T9 g; }" l
- if(!$uid){
% i# T7 t, @& }8 z - $data['msg']='您没登录,请先登录!';
|$ d c, n0 M6 T0 u- O8 I - }else if(date('Y-m-d') != '2017-12-12'){( P, J6 M0 W0 |6 P" u7 _
- $data['msg']='不在活动时间内!';
; |. u% R7 \% l; B2 x - }else{
/ t5 y6 W+ ?$ N3 V7 C - $hours=date('H');//当前小时数4 [$ s# E) c/ D j5 j0 w
- if($hours > '09' || $hours > '17'){) h. @2 F$ j, ]1 u6 ~9 \8 B
- 5 z4 Z$ c- \, L3 f9 e3 K, w
- if($lingqu == 1 || $lingqu ==2){//点击10点的
% x: G- g- ^2 o: u - if($lingqu == 1){
: f7 [6 v7 X# o/ G - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过
* X$ m! i1 g; j! d - if($hours > '09'){
4 d' @" C7 L* | - $num=mt_rand(25,28);//优惠券金额: V; j& x9 E" z* o) I6 y3 p6 M
- $id=1;% O" v/ B5 l5 d: r4 Z! Q
- }
V- ^* m3 H5 r, C: V* j! F! l! k( a - }else if($lingqu == 2){
. u/ E7 a4 H9 `9 X - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
$ J L, E: v( i1 A - if($hours > '17'){
5 ]; K0 Q7 F% D% s- i: E - $num=mt_rand(50,55);//优惠券金额
. s; q+ N' q1 ] - $id=2;
6 f& b; Q( r* ^ - }
0 |. p( W8 Q, b( R - }
@1 g! I; l- x8 G' F - if(!$id){/ Q$ X$ c; E" x* t
- $data['msg']='时间还没到,晚点再来吧。';" o% A5 Z4 O2 x; M0 ]9 c, b* l
- }else{
/ @) D; n1 T8 [3 D6 ^4 ? - if($is_lingqu){
) r1 r+ E1 u" h7 c3 ] - $data['msg']='你已经领取过了,留点给别人吧!';
( ~* l% F* r: T2 b9 l. q - }else{
2 _5 I5 T& K* }5 C0 l! Q; y" X - //锁表
/ s9 w2 f2 J, Y" u: _+ v# G$ h - M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
T6 A" }1 Y7 M x, B8 G3 X9 [ - $active=M('active_num')->where(array('id'=>$id))->find();; B6 R+ H0 G. l+ i% Z
- if($active > 0){' z; Z) Y2 j @9 E. t0 |' x
- //开启事务5 G X2 i ] ]
- M()->execute('start transaction');$ I" d Y% r6 P/ P. s- \# b7 R$ T2 C
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
5 u* L! }- f# I, s; I - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
5 X* W4 K0 D7 h [7 _6 t8 T - $members_preferential = M('members_preferential');
9 u8 k. q/ S4 x) j, W - //对应投资金额,
- c: |7 |8 A N$ f h; f1 e - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
1 L- @& r' m1 X( L$ c! I - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');0 ]: v% Y# w) a$ w
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间- T+ F0 q& Y5 o
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券1 n( J9 K0 }! }3 M
- if($save && $add && $add2){1 K! ~. w# Q9 W' L5 S: J- R1 {) o3 D
- //事务提交
9 l$ e s+ D/ V - M()->execute('commit');+ q# N: {( {0 g3 o2 W; n
- $data['msg']='恭喜你领取了'.$num.'元优惠券';& g7 D4 W& e8 F7 Q1 }
- }else{
3 G0 o3 {; T1 P4 B - //回滚$ F% j9 c+ Z* {
- M()->execute('rollback');
8 M1 q5 L/ G9 `2 x7 T3 S - $data['msg']='未知错误!';
9 r3 W2 i) H, Y8 O+ o% r - }
8 X# ` F# q( b! ~ - }else{
4 D! h" O( b: n# g - $data['msg']='红包已领完,你来晚了!';6 a3 M, T- h" J" u
- }
5 [7 t) Q! E1 G/ X* ^6 f/ v - M()->execute('UNLOCK TABLES');; U/ } w4 N, ] J
- }6 L; m9 S g+ m* O% f9 R
- }+ j4 e* |# r! X2 v
- }else{
% D2 y& J* E0 O - $data['msg']='非法操作!';5 T0 g! y/ K( F* L! a
- }8 n* K6 q( p$ F' d4 ]; L
- }else{2 o2 a. p' Y5 Y8 v( M6 ?
- $data['msg']='还没有到活动时间,请晚点再来哟!!';
8 [& u$ a- d: V X - }
# Q6 u$ O6 }7 i3 R - }0 b, h# ]/ @5 C# t$ {+ i
- exit(json_encode($data));# [# M/ a) E2 ^& L# ^1 `
- }
复制代码
: ~ R/ J2 s% [5 ]
' @" Q, M' w% c6 C) e9 U: ~4 V* B. s
欢迎光临 cncml手绘网 (http://bbs.cncml.com/) |
Powered by Discuz! X3.2 |