当前位置:网站首页>php使用redis实现分布式锁
php使用redis实现分布式锁
2022-07-06 06:09:00 【Miapenso】
最近在做一个领券功能的时候,发现在一定并发下会出现重复领券的问题。使用度娘一顿搜索操作之后,发现可以使用分布式锁来解决这个问题。
什么是分布式锁
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,常常需要协调他们的动作。如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互斥来防止彼此干扰来保证一致性,这个时候,便需要使用到分布式锁。
实现原理
实现分布式锁的原理很简单,就是需要有一把锁,多个服务同时去获取锁,但是只有一个服务能获取到锁。获取到锁的服务就可以执行自己的业务,没有获取到锁的其他服务需要等待获取到锁的服务业务执行完成后释放锁,然后再次尝试获取锁。
实现分布式的方案有很多种。如下
- 基于数据库实现分布式锁,比如mysql
- 基于缓存实现分布式锁,比如redis
- 基于Zookeeper实现分布式锁
这里我们使用redis来实现分布式锁,在执行业务之前先获取一个key,如果key存在就说明已经有其他服务获得锁,这个时候需要等待或者返回系统繁忙。如果key不存在,说明没有其他服务获取锁,把这个key保存到redis,然后执行业务,等待业务执行完就从redis中删除这个key。
php实现代码
<?php
class RedisLock
{
protected $redis;
public function __construct(){
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
$this->redis = $redis;
}
public function getLock($key){
$value = $this->redis->get($key);
return $value;
}
public function setLock($key,$value){
$this->redis->set($key,$value);
}
public function delLock($key){
$lineNumber = $thid->redis->del($key);
return $lineNumber;
}
}
$key = 'your_lock_key';
$value = time();
$redisLock = new RedisLock();
$isLock = $redisLock->get($key);
if($isLock) {
//已有锁,直接返回,不往下执行了
return false;
}
//没有锁,加锁
$redisLock->setLock($key,$value);
//todo 执行业务逻辑
sleep(5);
// 解锁
$redisLock->delLock($key);
使用ab进行测试
加锁
加锁
加锁
加锁
加锁
加锁
加锁
加锁
执行业务
解锁
解锁
执行业务
解锁
执行业务
解锁
执行业务
解锁
解锁
执行业务
解锁
加锁
执行业务
解锁
加锁
执行业务
解锁
从测试结果来看,发现有多个执行业务,并没有完全锁住。这个是因为我们用的是redis的set命令。set 命令用于设置给定 key 的值。如果 key 已经存储其他值, SET 就覆写旧值,且无视类型。这样会导致很多服务都能加锁成功,而我们想要的是只有一个服务能加锁成功。
要解决这个问题,需要了解redis的另一个命令setnx。setnx 命令在指定的 key 不存在时,为 key 设置指定的值。
<?php
class RedisLock
{
protected $redis;
public function __construct(){
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
$this->redis = $redis;
}
public function getLock($key){
$value = $this->redis->get($key);
return $value;
}
public function setLock($key,$value){
return $this->redis->setnx($key,$value);
}
public function delLock($key){
$lineNumber = $thid->redis->del($key);
return $lineNumber;
}
}
$key = 'your_lock_key';
$value = time();
$redisLock = new RedisLock();
$isLock = $redisLock->get($key);
if($isLock) {
//已有锁,直接返回,不往下执行了
return false;
}
//没有锁,加锁
$setLock = $redisLock->setLock($key,$value);
if(!$setLock) {
//加锁失败
return false;
}
//todo 执行业务逻辑
sleep(5);
// 解锁
$redisLock->delLock($key);
再次使用ab进行测试
加锁
加锁
加锁
加锁
加锁
加锁
加锁
加锁失败
加锁失败
加锁失败
加锁失败
加锁失败
已锁
已锁
已锁
执行业务
解锁
从测试结果来看,在未加锁的状态下,有多个服务同时获取加锁,但是只有一个加锁成功, 其他的都是返回加锁失败,再后面的服务更是直接返回已锁。由此可见,加锁成功。
那么到此就结束了吗?其实并不是的。假如在已加锁的情况执行业务,在业务过程中因为一些原因出现异常导致退出而没有进行解锁,那么将造成死锁,后面的所有服务都无法再次获取锁。为了解决这个问题,我们需要对锁设置一个过期的时间,防止死锁的发生。
<?php
class RedisLock
{
protected $redis;
public function __construct(){
$redis = new Redis();
$redis->connect('127.0.0.1',6379);
$this->redis = $redis;
}
public function getLock($key){
$value = $this->redis->get($key);
return $value;
}
public function setLock($key,$value,$second){
$setnx = $this->redis->setnx($key,$value);
if(!$setnx) {
return $setnx;
}
$expire = $this->redis->expire($key,$second);
if(!$expire) {
$this->redis->del($key);
}
return $expire;
}
public function delLock($key){
$lineNumber = $thid->redis->del($key);
return $lineNumber;
}
}
$key = 'your_lock_key';
$value = time();
$redisLock = new RedisLock();
$isLock = $redisLock->get($key);
if($isLock) {
//已有锁,直接返回,不往下执行了
return false;
}
//没有锁,加锁
$second = 5;
$setLock = $redisLock->setLock($key,$value,$second);
if(!$setLock) {
//加锁失败
return false;
}
//todo 执行业务逻辑
sleep(5);
// 解锁
$redisLock->delLock($key);
那么到此结束了吗?欢迎大家在评论区告诉我~
边栏推荐
- 职场进阶指南:大厂人必看书籍推荐
- HCIA review
- 浅谈专项测试之弱网络测试
- 数据库隔离级别
- Buuctf-[bjdctf2020]zjctf, but so (xiaoyute detailed explanation)
- Interface test: what are the components of the URL in fiddler
- Cannot create PoolableConnectionFactory (Could not create connection to database server. 错误
- Luogu p1460 [usaco2.1] healthy Holstein cows
- 黑猫带你学UFS协议第18篇:UFS如何配置逻辑单元(LU Management)
- H3C firewall rbm+vrrp networking configuration
猜你喜欢
随机推荐
selenium源码通读·9 |DesiredCapabilities类分析
How to recover Huawei router's forgotten password
公司视频加速播放
Sqlmap tutorial (III) practical skills II
Properties file
二维码的前世今生 与 六大测试点梳理
Huawei BFD configuration specification
[postman] collections - run the imported data file of the configuration
[C language] string left rotation
黑猫带你学UFS协议第8篇:UFS初始化详解(Boot Operation)
G - Supermarket
曼哈顿距离和-打印菱形
GTSAM中ISAM2和IncrementalFixedLagSmoother说明
[API interface tool] Introduction to postman interface
Embedded point test of app
「 WEB测试工程师 」岗位一面总结
nodejs实现微博第三方登录
数字三角形模型 AcWing 1015. 摘花生
LeetCode 729. 我的日程安排表 I
Interface test: what are the components of the URL in fiddler