暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

php 使用redis锁限制并发访问类

原创 kerosene 2021-10-22
1093



1.并发访问限制问题

对于一些需要限制同一个用户并发访问的场景,如果用户并发请求多次,而服务器处理没有加锁限制,用户则可以多次请求成功。

例如换领优惠券,如果用户同一时间并发提交换领码,在没有加锁限制的情况下,用户则可以使用同一个换领码同时兑换到多张优惠券。

伪代码如下:

1 if A(可以换领)
2     B(执行换领)
3     C(更新为已换领)
4 D(结束)

如果用户并发提交换领码,都能通过可以换领(A)的判断,因为必须有一个执行换领(B)后,才会更新为已换领(C)。因此如果用户在有一个更新为已换领之前,有多少次请求,这些请求都可以执行成功。 

2.并发访问限制方法

使用文件锁可以实现并发访问限制,但对于分布式架构的环境,使用文件锁不能保证多台服务器的并发访问限制。

Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。 
本文将使用其setnx方法实现分布式锁功能。setnx即Set it Not eXists。 

语法:

SETNX key value

将 key 的值设为 value ,当且仅当 key 不存在。

若给定的 key 已经存在,则 SETNX 不做任何动作。

SETNX 是『SET if Not eXists』(如果不存在,则 SET)的简写。

时间复杂度:
O(1)
返回值:

设置成功,返回 1 。

设置失败,返回 0 。

 

RedisLock.class.php

复制代码

 1 <?php
 2 /**
 3  *  Redis锁操作类
 4  *  Date:   2016-06-30
 5  *  Author: fdipzone
 6  *  Ver:    1.0
 7  *
 8  *  Func:
 9  *  public  lock    获取锁
10  *  public  unlock  释放锁
11  *  private connect 连接
12  */
13 class RedisLock { // class start
14 
15     private $_config;
16     private $_redis;
17 
18     /**
19      * 初始化
20      * @param Array $config redis连接设定
21      */
22     public function __construct($config=array()){
23         $this->_config = $config;
24         $this->_redis = $this->connect();
25     }
26 
27     /**
28      * 获取锁
29      * @param  String  $key    锁标识
30      * @param  Int     $expire 锁过期时间
31      * @return Boolean
32      */
33     public function lock($key, $expire=5){
34         $is_lock = $this->_redis->setnx($key, time()+$expire);
35 
36         // 不能获取锁
37         if(!$is_lock){
38 
39             // 判断锁是否过期
40             $lock_time = $this->_redis->get($key);
41 
42             // 锁已过期,删除锁,重新获取
43             if(time()>$lock_time){
44                 $this->unlock($key);
45                 $is_lock = $this->_redis->setnx($key, time()+$expire);
46             }
47         }
48 
49         return $is_lock? true : false;
50     }
51 
52     /**
53      * 释放锁
54      * @param  String  $key 锁标识
55      * @return Boolean
56      */
57     public function unlock($key){
58         return $this->_redis->del($key);
59     }
60 
61     /**
62      * 创建redis连接
63      * @return Link
64      */
65     private function connect(){
66         try{
67             $redis = new Redis();
68             $redis->connect($this->_config['host'],$this->_config['port'],$this->_config['timeout'],$this->_config['reserved'],$this->_config['retry_interval']);
69             if(empty($this->_config['auth'])){
70                 $redis->auth($this->_config['auth']);
71             }
72             $redis->select($this->_config['index']);
73         }catch(RedisException $e){
74             throw new Exception($e->getMessage());
75             return false;
76         }
77         return $redis;
78     }
79 
80 } // class end
81 
82 ?>
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论