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://请求的脚本
复制代码
" I& e6 r9 c; s. w( _( AMysql中的锁语法:
( A% b" Z( [ WLOCK TABLE 表名1 READ|WRITE, 表名2 READ|WRITE .................. 【锁表】
8 x: E2 K- _8 C/ GUNLOCK TABLES 【释放表】
Read:读锁|共享锁 : 所有的客户端只能读这个表不能写这个表
8 G( |6 P; }* Y' O& u. W" XWrite:写锁|排它锁: 所有当前锁定客户端可以操作这个表,其他客户端只能阻塞% D+ c7 ]- e4 y( G% _7 Q
注意:在锁表的过程中只能操作被锁定的表,如果要操作其他表,必须把所有要操作的表都锁定起来!
PHP中的文件锁 :5 U; [$ I! Z- B1 g8 [" F& q
文件锁的文件与表有什么关系?:一点关系也没有,与令牌相似,谁拿到谁操作。所以表根本没锁。- p; Y/ r$ x. b& X, K3 Z- }% D) ?. }
测试时,有个文件就行,叫什么名无所谓
总结:$ P w# R! {; w; E! v: x
项目中应该只使用PHP中的文件锁,尽量避免锁表,因为如果表被锁定了,那么整个网站中所有和这个表相关的功能都被拖慢了(例如:前台很多用户一直下订单,商品表mysql锁表,其他与商品表相关的操作一直处于阻塞状态【读不出来商品表】,因为一个功能把整个网站速度拖慢)。
比如在一个O2O外卖项目中,中午12-2点,晚上6点都是订单高并发时,这种情况下,MySQL锁显然是不考虑的,用户体验太差。其实根据实际的需求,外卖可以不用设计库存量的,当然除了秒杀活动模块还是需要php文件锁的。
应用场景:) s- v3 {* t6 T Y+ I) a
1. 高并发下单时,减库存量时要加锁! ]- M) _) C. \( Y7 E; A
2. 高并发抢单、抢票时要使用
Mysql锁示例:
- /*$ y; P3 ?6 U4 ~5 I" f* Z
- 模拟秒杀活动-- 商品100件. n1 @9 U0 J) x' b6 c1 E
- CREATE TABLE ta# o5 C% f( w* U; t" A( Y
- (& {1 A: D( U5 {7 v. ?. V
- id int comment '模拟100件活动商品的数量'
4 t! I6 D! K% W0 Z A3 C7 D# y) Q6 j - );. G4 p Y* |/ a: f9 D
- INSERT INTO ta VALUES(100);
N) D& f) u2 T1 s1 L - 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件
% ~; e5 H5 B: t! ~3 A8 a" X - */ ! x: G8 n5 e _6 P M
-
6 Y9 T1 x2 b" H& b; L, B6 } - // 关闭错误报告
7 W# J$ S9 j4 H- L/ v - error_reporting(0);
4 w6 h' ?$ N; t b - 7 j& |" S# b6 L, O8 }+ @
- $dbhost = 'localhost:3306'; // mysql服务器主机地址
) N: `3 N6 `! ` - $dbuser = 'root'; // mysql用户名
9 w# p" u: b6 b$ \4 y - $dbpass = 'root'; // mysql用户名密码
3 r, E3 |5 e9 M# G6 E! c) ~ - $conn = mysqli_connect($dbhost, $dbuser, $dbpass);; F4 t4 o% M, C v, G8 u- k
- if(! $conn )
3 v1 L. }! [' _* A1 u/ [5 ]- b1 W8 M - {
' |8 x2 J: T; l' R1 M8 Z6 i - die('连接失败: ' . mysqli_error($conn));. \. }, i6 k) V1 Y0 ^
- }
5 J- s0 }- t# }' l; V( T: g" V" j4 ` - // 设置编码,防止中文乱码
! N9 _5 j! t! T, X3 {- Z; Q - mysqli_query($conn , "set names utf8");' L5 e5 m- `+ a% r1 |, l6 z
- mysqli_select_db( $conn, 'temp' ); ) H+ b9 J) O% \) ?
- ( \ \3 |! x$ ]
- # mysql 锁
5 G$ y: _$ X9 b( ~4 M+ n6 | - mysqli_query($conn , 'LOCK TABLE a WRITE');// 只有一个客户端可以锁定表,其他客户端阻塞在这 * b; G8 \ r" o" Y5 y" U6 f% n
- $rs = mysqli_query($conn , 'SELECT id FROM a'); ( F7 D3 f# J& F3 ~) U
- $id = mysqli_result($rs, 0, 0); / r+ s: _" q3 B
- if($id > 0) / `3 Q8 _6 X4 h
- { # u" J6 i" J/ d& \
- --$id;
$ B, x) b8 ?) l `5 p, l5 F - mysqli_query($conn , 'UPDATE a SET id='.$id);
2 x# z5 |- P. n6 \* [, J - } 8 p) Y# ?8 U# ~! o: P S" K# C
. E' [. \$ ]2 F9 V( K# p- A/ J' y- # mysql 解锁
* w' I2 Z6 W" `2 R& @ - mysqli_query($conn , 'UNLOCK TABLES');1 u( d7 ?) B+ n
- //查询解锁后的id值
+ f' o1 k5 V2 C8 e! J - // $res = mysqli_query($conn , 'SELECT id FROM ta');' _9 g0 E$ H+ y0 ~
- // while($row = mysqli_fetch_assoc($res))
( [( B" I* z% a - // {1 d( G# `- |6 c0 a6 D, L
- // $id = $row['id'];. l, |' X) A+ t
- // }
& v# r" `# R: C4 k0 \- p) V, g - // echo $id;
复制代码 2 \. J! j3 D/ o) u
) Z6 p! d' c! C2 o) ?" p
- d" ^ O, R% ]8 K, RPHP文件锁示例:
- /*
# T `4 z) R# N$ R - 模拟秒杀活动-- 商品100件
$ T0 r Z5 \8 N - CREATE TABLE ta; F# L2 X/ p; Q# E: X
- ($ @ T4 D0 Q5 C
- id int comment '模拟100件活动商品的数量'
8 U( G2 b ]" @6 c6 b0 L4 P - );( L! t& |; |: C ?; S, B5 U: d2 ]/ q
- INSERT INTO ta VALUES(100);' H7 c0 Y5 ^, L* ?7 _1 F
- 模仿:以10的并发量访问这个脚本! 使用apache自带的ab.exe软件% K3 |5 T( ]' m' O( ~& g
- */
4 C6 j$ c0 a5 }: q9 G O1 { -
/ ?) I- T* J( A& |4 |5 g - // 关闭错误报告
* d _ P7 I$ b" o& o - error_reporting(0);
& V9 o% A1 L" A/ T
6 F( H; X3 M! {0 Q' {- $dbhost = 'localhost:3306'; // mysql服务器主机地址. C/ q. N! _+ c4 [6 [; G! u
- $dbuser = 'root'; // mysql用户名
5 C& f, q9 \, ?: ~1 M - $dbpass = 'root'; // mysql用户名密码" s% G0 |8 X3 e% C) ]' V+ K2 C$ ^
- $conn = mysqli_connect($dbhost, $dbuser, $dbpass);
/ F2 h4 [' w6 K - if(! $conn )3 e3 U+ p6 a: b5 g L
- {, Y( R0 H) } I
- die('连接失败: ' . mysqli_error($conn));, X% [/ a- Z' k! _% O8 s
- }+ N% G/ t0 p/ v2 n
- // 设置编码,防止中文乱码
' r6 D! S. U' }! q& O" m - mysqli_query($conn , "set names utf8");
. X& K; B, R& ? - mysqli_select_db( $conn, 'temp' );
) R0 H) T8 e* G- b" r L
J1 L2 ?$ d! M. M+ O5 L- # php中的文件锁 & _4 R' k ?! ?% m
- $fp = fopen('./a.lock', 'r'); // php的文件锁和表没关系,随便一个文件即可 , ]* g2 A2 k0 ~
- flock($fp, LOCK_EX);// 排他锁 7 n0 E0 u# B2 r# ~1 n Y: {
. z6 L: \4 F4 {+ q) X- $retval = mysqli_query($conn ,'SELECT id FROM ta');
, ]; @0 R+ L. a' `" X* ]) z - while($row = mysqli_fetch_assoc($retval))
1 c4 `) ^4 |9 V, V - {+ l# ^3 S5 X# z* U# F8 R
- $id = $row['id'];
0 P& Y# ~. X+ X# T0 t: {+ V - }4 x4 y- V F7 V& ?
-
3 b4 y! n" ?( ` - if($id > 0)
, z! o6 k5 [& ~- b$ Q - { % Q* S: H: I' U) ?: }! F! C4 U
- --$id; # u& h7 _, J/ \+ n& C6 ]+ K5 a
- mysqli_query($conn ,'UPDATE ta SET id='.$id);
0 \' |' @' f+ v9 W( U - }
$ ^* @9 r9 F. @. ]' o5 X$ ~1 D' U - # php的文件锁,释放锁 5 X" Z. t9 `+ f9 B5 W- Z$ C' N4 `
- flock($fp, LOCK_UN);
6 C/ r( f3 P' ]" o2 o/ k - fclose($fp);
2 [4 A7 M j1 u- \: J. K
' ^& [1 X# F2 x7 R, S4 E- // $res = mysqli_query($conn , 'SELECT id FROM ta');
$ B& G5 `" p- @- ?; @ - // while($row = mysqli_fetch_assoc($res))& O* l* D( F# U# y% m* m
- // {
9 o) |( _( b4 w, B* z - // $id = $row['id'];
4 R+ ^4 O; N1 n2 V5 r. e - // }
2 G. G: T7 v8 _' C7 D - // echo $id;
复制代码 - h: i- j0 E, w1 [4 P. I1 d
3 @9 ?' j9 r$ ]( c& p# S9 [ c2 \1 K
抢券活动实例:
- public function envelopeSnatching(){
+ z! C) W7 H% D5 b) O. Y4 ` - $lingqu = $_POST['type'];
$ `, @3 t7 n: O6 T1 \ - $uid=session('u_id');//用户id
$ k3 Z) W+ ~6 j: \) l - if(!$uid){
& B3 s j* n7 C" g - $data['msg']='您没登录,请先登录!';
3 M* [( C; d8 g" i: H - }else if(date('Y-m-d') != '2017-12-12'){+ J2 ^: \; |5 U4 N: x( `
- $data['msg']='不在活动时间内!';/ S# Y" W1 Y( e! a- U4 ?5 z
- }else{- F6 r$ W1 N" g6 I4 b' g* |
- $hours=date('H');//当前小时数! |3 Z5 ^$ {/ H1 i, J4 i
- if($hours > '09' || $hours > '17'){
0 H% l& j8 U) o/ e3 w
7 g* t1 |, R# P& y& @- w- if($lingqu == 1 || $lingqu ==2){//点击10点的
; v: K9 i5 l* ]( R7 G, M6 E - if($lingqu == 1){3 {; z$ O( @1 S0 e/ }' ?. f
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>1,'uid'=>$uid))->find();//判断是否领取过0 X, e t; V) p. `9 e! E9 |
- if($hours > '09'){
6 m4 I! b* c# _7 A5 d' m( Y - $num=mt_rand(25,28);//优惠券金额: L, c7 P# u7 [. y! H9 W- V
- $id=1;
2 P9 r U( c6 K3 b - }
" `7 Z; H" |. P9 a, ~* { y, ~4 o) d - }else if($lingqu == 2){# V$ r+ g' n$ x, h
- $is_lingqu=M('active_log')->field('id')->where(array('num_id'=>2,'uid'=>$uid))->find();
! g& e$ J& S% |: X; b2 T b4 O; r - if($hours > '17'){
# T7 }; ?& b7 Y, ]0 y - $num=mt_rand(50,55);//优惠券金额
2 y }, N( @: T8 X4 ]2 ^# u% V - $id=2;1 C; m8 {4 v& l2 [: I: P
- } v7 }0 R. k# O# q$ j9 n/ C
- }
! P$ G5 w6 D! J# ? - if(!$id){" ?3 c1 V) C4 K$ P( k. Z. }; d
- $data['msg']='时间还没到,晚点再来吧。';
. K7 [& Y, a: ~; X+ |+ `' z2 W - }else{1 `, Z9 o0 }, l. E8 s& D1 W4 l, X
- if($is_lingqu){, Z z% y/ N% F1 E6 l& h7 R
- $data['msg']='你已经领取过了,留点给别人吧!';
" y. f) }7 s- K3 m - }else{
# @" s, Y$ h; G - //锁表: v# `0 n6 ^! L/ u( g" z2 K: B
- M()->execute('LOCK TABLE active_num WRITE,active_log WRITE');
9 @: \. k* s/ O* B - $active=M('active_num')->where(array('id'=>$id))->find();1 _ c t2 G! u8 L& Y# n
- if($active > 0){
. G$ L% x3 ^0 R+ v7 K - //开启事务
, i2 o _# f+ w( H' V. }2 | - M()->execute('start transaction');. Y) Y+ ~- [' E
- $save=M('active_num')->save(array('id'=>$id,'num'=>$active['num']-1));# }1 U* q6 s5 H2 {, d# Q. K: d4 q
- $add=M('active_log')->add(array('uid'=>$uid,'money'=>$num,'num_id'=>$id,'addtime'=>time()));
+ C" G- |3 j, \7 s - $members_preferential = M('members_preferential');( N& x9 t( H/ M: J
- //对应投资金额,
& L5 H# a. `. N1 F( |4 `: |; a - $key=array_search($num,array_reverse(C('PREFERENTIAL_JL'),true));
" X' a: ]7 R- m8 E' v* ^ - $PREFERENTIAL_ENDDAY=C('PREFERENTIAL_ENDDAY');
5 Z V1 W/ \: J1 Q8 l - $endtime=strtotime("+{$PREFERENTIAL_ENDDAY}day");//过期时间: Z. [/ i J( c9 y
- $add2=$members_preferential->add(array('uid'=>$uid,'type'=>$key,'endtime'=>$endtime));//添加优惠券1 [4 X+ N; ]% y p" Z, U- I" C- z R
- if($save && $add && $add2){0 m( m/ D7 ~6 K1 i/ _# c
- //事务提交3 k" H" I+ B) H, E4 e a% j
- M()->execute('commit');1 }; h3 x0 H3 Z8 Q+ o6 u' b
- $data['msg']='恭喜你领取了'.$num.'元优惠券';( ]& z- w9 j& D" L- u5 {
- }else{4 n; Q; C4 f) p2 T+ G
- //回滚
+ W! n4 O! Z" D5 `( u - M()->execute('rollback'); O( B3 ]% a3 s: P
- $data['msg']='未知错误!';
8 J7 @( H* \9 x' P9 ? - }
2 g9 X1 _& t$ J0 @* _9 k - }else{6 J a, S7 n1 B
- $data['msg']='红包已领完,你来晚了!';8 p3 m& [/ F5 v. u
- }9 k* w$ r" X, g* x: T3 ]
- M()->execute('UNLOCK TABLES');
5 H0 r# H8 X6 V+ s+ z/ _ - }: n9 X u9 Z, G' l
- }- O7 D1 N/ H4 Z- G5 I, @5 K
- }else{' J0 y! j7 r' y, Z- q
- $data['msg']='非法操作!';0 l' s. E6 c: `* z, T: h
- }
# C5 Y9 q! y0 ]+ e - }else{( U3 M% Q4 D2 y2 }: d
- $data['msg']='还没有到活动时间,请晚点再来哟!!';1 o) e/ @) K; P, [5 c$ n
- }# @- c2 ?: j$ V" o
- }
" }5 _ m* C* v! t$ _2 g - exit(json_encode($data));
" I( o4 S( r2 ?5 Q% v0 W$ q - }
复制代码
" Q) p7 V2 x7 V' P3 U
, ?& ^' v, b7 Q/ M
| 欢迎光临 cncml手绘网 (http://www.cncml.com/) |
Powered by Discuz! X3.2 |