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 B A0 B L; a
Mysql中的锁语法:+ _. O9 p! h. R* o
LOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】) `7 P' ]7 F$ U4 d, \
UNLOCK TABLES 【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
4 O& f6 G% W! w& `Write:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞- h: u6 n9 s3 q, w4 O
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :! O4 J6 m$ p6 a
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。6 n4 y, W2 s- I
测试时,有个文件就行,叫什么名无所谓
总结:1 v- l* D+ x5 ^
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:
( _7 |) w+ ~0 W: s. v9 o1. 高并发下单时,减库存量时要加锁
: B2 z: @7 f; Y2. 高并发抢单、抢票时要使用
Mysql锁示例:
- /*" x9 Z( t- j& X7 ?
- 模拟秒杀活动-- 商品100件
* l3 y% |. \5 z5 u* N - CREATE TABLE ta
6 k9 s, r- t7 y* S - (
* q5 V4 T; R; _( g0 P* L( e - id int comment '模拟100件活动商品的数量'; z- w( a7 v5 `+ ~& \% L5 u! D
- );
8 h% U4 j: p: i* H( p - INSERT INTO ta VALUES(100);
6 w, R" b; n7 }; T - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件+ Q/ b' X! @' q! H, h. R2 W/ f
- */
# n$ S) ^0 y8 O! f8 Y+ ~, V4 u0 h - $ i. F+ {& {0 e( w4 A
- // 关闭错误报告& }3 t% w+ E8 t! V7 \4 {
- error_reporting(0);
: |8 b) f$ R( J7 |9 B, M
( x. K! ]7 R. }4 i( v5 j! _. X- $dbhost = 'localhost:3306'; // mysql服务器主机地址( |+ d1 z* T3 A. ^
- $dbuser = 'root'; // mysql用户名& Z& \1 b @9 ^' }+ }
- $dbpass = 'root'; // mysql用户名密码
# p& T: |: m6 `2 d5 J - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);/ b6 N' v, W6 X/ M8 }; ^3 N; X8 }1 k
- if(! $conn )4 r6 O4 X/ z3 J4 v/ S/ j8 z
- {- _. M* R2 o- s# v4 n0 Z( H
- die('连接失败: ' . mysqli_error($conn)); f: o- ? H8 G1 r
- }5 s' T4 C- k/ R0 }9 l+ k
- // 设置编码,防止中文乱码
+ i) K! M4 p: K* N# _, q3 D* D - mysqli_query($conn , "set names utf8");
; P( s9 \: ` Q1 c, C - mysqli_select_db( $conn, 'temp' ); ; D2 l5 ?8 B6 L( f' l8 D
- ) i- p6 y7 r4 O& p* x& }. j
- # mysql 锁
$ p+ p5 D v+ r0 G- V6 V {& } - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 " G6 H% t5 C1 J) {0 z. T
- $rs = mysqli_query($conn , 'SELECT id FROM a');
+ R n* z5 L. m# p8 L# o2 X: ` - $id = mysqli_result($rs, 0, 0); $ X( N0 Y2 d* x5 H$ R$ D& k
- if($id > 0)
* \5 U2 G/ I2 ]; G - { 4 L' `9 U3 h, ?2 ?4 H
- --$id; ! a1 _' H/ F+ o2 t9 o f- u1 d" h
- mysqli_query($conn , 'UPDATE a SET id='.$id);
* f. k, {7 s' n0 ~% S - } 1 K* h& S9 N7 _- |5 E' J& O) d
7 f c# f! U$ h' f- # mysql 解锁
+ x7 n2 S$ H3 Z - mysqli_query($conn , 'UNLOCK TABLES');+ x/ B7 y: I4 w3 U; q4 Q7 B0 w
- //查询解锁后的id值! R1 ?! D: p8 e6 k' b4 X3 g# \$ Y) I
- // $res = mysqli_query($conn , 'SELECT id FROM ta');
: c. ~. [& Z; @0 D - // while($row = mysqli_fetch_assoc($res))$ \5 d/ m6 D" y! ]! V* [1 V6 {
- // {
" I T. a, c+ t$ o1 Y) @" T - // $id = $row['id'];
3 T$ K- ~9 U6 z - // }
% T1 v# o; {. Y - // echo $id;
复制代码 / Q6 G$ o2 H* i- G2 P
1 Q! e2 e" U1 j6 t
( q+ g$ h- `6 `9 p% K2 y& KPHP文件锁示例:
- /*- O# v" T0 o k! S$ m( n8 G
- 模拟秒杀活动-- 商品100件
8 B6 _8 c: B5 m6 E - CREATE TABLE ta+ u& N+ R7 N$ f+ Z$ ^
- (
8 w3 f/ K8 B+ R, W% @! E. l - id int comment '模拟100件活动商品的数量'; v8 J" P9 i6 b6 y, P4 F
- );
) q4 c8 |5 U: F9 z3 L0 E$ F - INSERT INTO ta VALUES(100);
- [$ \* @' ~$ G. W1 ~ - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
% ?; {, F8 q$ H* ~ - */
% @( {; x F4 A2 H - % H1 Q* g: M4 ]" s
- // 关闭错误报告2 k1 A$ e: z4 I; E1 ~5 i" S0 w
- error_reporting(0);
: H: @; o. g1 e& n% k: h - ; J0 {" [+ _; p# P! X0 T' s
- $dbhost = 'localhost:3306'; // mysql服务器主机地址/ P& J8 A( [" H* Y6 k0 ^
- $dbuser = 'root'; // mysql用户名/ s$ M. u2 e! d$ T) z- G
- $dbpass = 'root'; // mysql用户名密码/ t6 u4 W* ^* e3 O. L
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
7 j9 V6 e$ T4 U: ~" u; }/ o4 j - if(! $conn ): i$ @* H+ }4 p" A
- {& s8 W' L& G) ~8 x; p& p$ a4 l' {
- die('连接失败: ' . mysqli_error($conn));' d6 @. G; q" G2 {! F& I
- }( l0 f8 [3 c, I1 ]5 ?+ h
- // 设置编码,防止中文乱码4 ?. R3 e2 `( b% s* Z0 S
- mysqli_query($conn , "set names utf8");
* ^1 x$ [' j( D: T' h" ]( k$ p) ] - mysqli_select_db( $conn, 'temp' );
7 t% ]$ V+ w( y6 f7 g1 u - 8 i" @: |; R2 x$ w0 U/ i
- # php中的文件锁
( Q4 g1 D" |7 Y' d, q* H6 r - $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可
( j! ]2 N) d# w) A3 c8 b - flock($fp, LOCK_EX);// 排他锁
4 C, I* m) |$ g" W8 e8 K3 C- a - j) O. V5 c2 _, k. [ E/ C% {' r; E
- $retval = mysqli_query($conn ,'SELECT id FROM ta'); 7 h4 W3 ~4 }$ T2 E+ n
- while($row = mysqli_fetch_assoc($retval))
4 v, U6 l* I$ ?5 n. ?& n4 F - {1 o& |0 u5 W; ]( b& I& N O) K
- $id = $row['id'];
! j# L. s8 b2 l8 e9 S' L - }) ~' C B4 b: y9 b7 K( U
-
2 ], o( g; @: h0 [5 `4 V, { - if($id > 0) ( Y* _" | x% h) ^3 x
- { , E4 t8 [0 [/ ~9 d. S! ]
- --$id; $ h; O* B$ `0 N6 f
- mysqli_query($conn ,'UPDATE ta SET id='.$id); % Y3 j$ O# L. j8 B: S
- }
- ^4 F4 q- T8 h4 C - # php的文件锁,释放锁
3 T8 u4 i9 O+ j) ^/ ^ - flock($fp, LOCK_UN); $ a V; O+ b8 y
- fclose($fp);
! b/ c" D* W2 p, r* G
9 W4 e0 S' F* c$ [! ]. e- // $res = mysqli_query($conn , 'SELECT id FROM ta');
, D4 i# z8 |/ g/ z - // while($row = mysqli_fetch_assoc($res))
! `" ^ @5 g& [/ J1 c) } - // {" E+ a, v9 J0 a, t/ k
- // $id = $row['id'];8 D4 ^. a+ g2 P* y1 [" Q$ ?
- // }" `( |4 b/ t3 @/ b* X5 F$ J) @
- // echo $id;
复制代码
- v7 l d, V! v
6 s1 j2 I: _$ r2 g: P, ]2 _抢券活动实例:
- public function envelopeSnatching(){. D& R* z. a) R1 J
- $lingqu = $_POST['type'];
# t5 V# C# _2 h* `/ F- [9 u - $uid=session('u_id');//用户id$ P6 |5 a5 \- U7 o: z8 {
- if(!$uid){8 F$ E0 S% C# @) j0 m1 y) o3 c
- $data['msg']='您没登录,请先登录!';
, Z$ R7 c- B, ]: r - }else if(date('Y-m-d') != '2017-12-12'){! w6 @' S6 d w% X6 I
- $data['msg']='不在活动时间内!';
5 |0 X, f# W/ J7 K2 v - }else{
- F" M R. C: j! p, F1 e1 M( b - $hours=date('H');//当前小时数# \. T/ p1 m: ?4 k# ]& J
- if($hours > '09' || $hours > '17'){
: S( g4 O, ]4 u* {1 \ - 1 @# p+ o' c' Y3 N, }& L
- if($lingqu == 1 || $lingqu ==2){//点击10点的, a& b' \7 O1 q1 p) ?$ z/ k
- if($lingqu == 1){) V1 ~: U) w U, g7 X n' w
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过) `% t! C) k* y
- if($hours > '09'){
3 V. _, [8 g' d8 N5 H - $num=mt_rand(25,28);//优惠券金额+ T/ f! h/ S; Q# Z! B4 q
- $id=1;( x" b; q" ]8 E( f
- }; e8 p; ?! f' D$ }
- }else if($lingqu == 2){
9 M; K# V6 h% n z% Y - $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();) ?1 Q. F6 i. r) P* @/ D2 H p" I
- if($hours > '17'){" P7 T7 D/ u+ Z
- $num=mt_rand(50,55);//优惠券金额' `6 L2 d) C' L+ w% k8 q0 C
- $id=2;; d! X1 I5 h6 f- j* k5 S4 l" c1 ]
- }6 Y2 K" V: {6 w8 W
- }+ ^' ~8 S' u- A. K1 R% Z5 m
- if(!$id){
7 d1 D5 ~& A4 t' y/ @ - $data['msg']='时间还没到,晚点再来吧。';
, O! u0 [- {! Q7 ^0 c - }else{
2 x0 h& w7 j$ G - if($is_lingqu){9 Z$ g% D) i) K4 X$ y; F6 a& D
- $data['msg']='你已经领取过了,留点给别人吧!';; r: C% }; x% p- Q/ a
- }else{6 P5 F3 ^& p4 t% ~( s. N" g
- //锁表$ l- @. p/ y9 G
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');' \6 @/ n9 S( Y: q; ~& r; q
- $active=M('active_num')->where(array('id'=>$id))->find();: e: b* b, r9 M/ B5 Z" \2 l0 ~$ o; }+ Y
- if($active > 0){
# H0 U( J2 h4 l - //开启事务0 u8 F [! @8 ^9 q! ^
- M()->execute('start transaction');6 f; \! k( c/ F: W. ?
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));
+ `( \) u, J% ?% t/ f& ` - $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
- i. g. F/ P. A: B% V - $members_preferential = M('members_preferential');$ @9 B4 _# ^% Y! ]) F9 ~" V. O
- //对应投资金额,
L" j- R7 r9 ^6 Z0 J - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));* {8 M' r' w& }8 B; X
- $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');: n+ X. c O: T2 ^% N3 s6 i, f
- $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间+ M9 b, j4 U3 u$ t1 B/ M
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券
3 r, r: D3 \: ?# O - if($save && $add && $add2){( i- E" y( U2 q" V
- //事务提交
- E7 J. h! x" Y4 e - M()->execute('commit');; \2 h: b7 ^5 [
- $data['msg']='恭喜你领取了'.$num.'元优惠券';
8 V$ B" Y9 `7 l - }else{; }' [* P1 q- [+ e5 a4 z) F
- //回滚1 k& b5 f+ _/ r
- M()->execute('rollback');
' x0 X! z$ c; W5 c1 N - $data['msg']='未知错误!';
* Q5 {3 \. ?( o0 ?. q4 j4 g - }
( B x/ |2 y9 H" f" N0 r - }else{
, y( e1 C7 D) T! |7 ~% h" z/ c - $data['msg']='红包已领完,你来晚了!';
% g* i& a7 _: a3 @+ Y$ O$ T2 Z: B - }
( K" m0 @$ Z6 u3 b5 u" E+ K2 Q* Y - M()->execute('UNLOCK TABLES'); z) f o9 G) {- P* }( V% I/ h L
- }
& z- e. b8 B C2 G% `$ ?: I9 C - }
- w: K/ g3 C: V9 N) @/ E) Y1 P! X7 y - }else{
2 b+ A6 k& w O5 f& n) m - $data['msg']='非法操作!';5 D& Y& K c0 r+ Z" b2 v7 S" p$ e* Z
- }
& ]8 p- H, s3 b" F- r - }else{
* D6 u. M" n! a6 B: b - $data['msg']='还没有到活动时间,请晚点再来哟!!';
( F/ u( m# f9 c6 [" w - }
9 H1 Z' i7 @4 s$ P1 Y o9 } m - }% v, R- l/ U4 ~5 v5 n( B9 ?# Q
- exit(json_encode($data));
8 e1 q9 V# V/ Z0 c$ H - }
复制代码 8 {# x$ g4 G. q% ^/ o
! W1 y9 F5 n9 }4 K7 y) ]+ w
| 欢迎光临 cncml手绘网 (http://www.cncml.com/) |
Powered by Discuz! X3.2 |