当前位置:网站首页>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);
那么到此结束了吗?欢迎大家在评论区告诉我~
边栏推荐
- 全链路压测:构建三大模型
- 【微信小程序】搭建开发工具环境
- 测试周期被压缩?教你9个方法去应对
- 假设检验学习笔记
- Luogu p1460 [usaco2.1] healthy Holstein cows
- The difference and usage between continue and break
- Accélération de la lecture vidéo de l'entreprise
- Software test interview questions - Test Type
- CoordinatorLayout+NestedScrollView+RecyclerView 上拉底部显示不全
- (5) Explanation of yolo-v3 core source code (3)
猜你喜欢
调用链监控Zipkin、sleuth搭建与整合
H3C V7 switch configuration IRF
Application du Groupe Li dans gtsam
全链路压测:构建三大模型
【eolink】PC客户端安装
MPLS test report
10m25dcf484c8g (FPGA) amy-6m-0002 BGA GPS module
CoordinatorLayout+NestedScrollView+RecyclerView 上拉底部显示不全
Buuctf-[[gwctf 2019] I have a database (xiaoyute detailed explanation)
10M25DCF484C8G(FPGA) AMY-6M-0002 BGA GPS模块
随机推荐
What are the test sites for tunnel engineering?
【Postman】测试(Tests)脚本编写和断言详解
MySQL之数据类型
[wechat applet] build a development tool environment
[postman] dynamic variable (also known as mock function)
H3C S5820V2_ Upgrade method after stacking IRF2 of 5830v2 switch
Expose the serial fraudster Liu Qing in the currency circle, and default hundreds of millions of Cheng Laolai
数学三大核心领域概述:代数
数据库隔离级别
【Postman】Monitors 监测API可定时周期运行
About PHP startup, mongodb cannot find the specified module
F - True Liars (种类并查集+DP)
Embedded point test of app
The ECU of 21 Audi q5l 45tfsi brushes is upgraded to master special adjustment, and the horsepower is safely and stably increased to 305 horsepower
Hypothesis testing learning notes
[C language] qsort function
LeetCode 739. 每日温度
Application of Lie group in gtsam
Manhattan distance and Manhattan rectangle - print back font matrix
异常检测方法总结