redis实现分布式锁

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: redis实现分布式锁

当服务运行在多台服务器时,为了避免服务器同时占用一个资源,或者重复处理同一个资源,我们需要通过 分布式锁 解决同一时间占用的问题,保证同一时间只有一台服务器处理某些逻辑.

redis 分布式锁

通过redis 单线程特性,很容易就能实现一个分布式锁方案:

image.png

锁超时

以上的流程,可能会造成这样的情况:

当服务器加锁成功之后,突然发生异常终止了进程,这样会导致锁永远无法释放.

解决:

所以,在redis set key value的时候,需要额外设置个过期时间,当进程异常/进程处理超时,redis key过期之后会自动释放锁,避免整个集群无法工作

跨服务删除键:

在加入锁超时逻辑之后,又会出现这样的情况:

- 服务器A成功 set key

- 服务器A 处理超时

- redis过期自动删除key

- 服务器B 成功 set key

- 服务器A 处理成功,去del key

- 服务器B的key被del

- 服务器C拿到锁.

....

这个时候,就会造成多台服务器同时在处理逻辑

解决:

- 在服务器 拿到锁之后, rand 一个随机数 randNum,redis set key randNum,将随机数存入key中

- 服务器A处理超时

- redis过期自动删除key

- 服务器B 拿到锁,rand一个随机数 randNum,redis set key randNum,将随机数存入key中

- 服务器A处理成功,获取 key的值,如果等于 服务器A的randNum,则删除,如果不等于,则不做处理

- 服务器B处理成功,获取key的值,如果等于服务器B的randNum,则删除

redis 原子性问题

在上面的  "先获取key的值,如果等于/不等于则删除"  这个逻辑时,由于redis的命令是单一命令问题

不能直接通过  get key,再判断del key实现,需要使用redis的eval lua脚本,进行实现:

if redis.call('get',KEYS\[1\]) == ARGV\[1\] then 
   return redis.call('del',KEYS\[1\]) 
else
   return 0 
end

调用方法:

eval "lua脚本字符串" 1 锁的key 随机数
eval "if redis.call('get',KEYS\[1\]) == ARGV\[1\] then return redis.call('del',KEYS\[1\]) else return 0 end" 1 pay-center-lock-key 90754863

redis set 命令的NX,PX

在新版本中,可以直接通过redis set 实现:"如果不存在key,则set,否则不做操作","set的时候设置过期时间"的操作

SET lock_key "123456" PX 1000 NX

完善的流程图


image.png

easyswoole php封装代码:

<?php
namespace App\\Utility;
use EasySwoole\\Component\\Singleton;
use EasySwoole\\Redis\\Redis;
use EasySwoole\\RedisPool\\RedisPool;
use EasySwoole\\Utility\\Random;
class RedisLock
{
    use Singleton;
    const KEY_PREFIX = 'pay-center-lock-key';
    const TTL = 60;
    public function lock($key)
    {
        return RedisPool::invoke(function (Redis $redis) use ($key) {
            $randNum = Random::number(8);
            $result = $redis->set(self::KEY_PREFIX . $key, $randNum, \['NX', 'PX' => self::TTL * 1000\]);
            if ($result) {
                return $randNum;
            } else {
                return null;
            }
        });
    }
    public function unlock($key, $randNum)
    {
        return RedisPool::invoke(function (Redis $redis) use ($key, $randNum) {
            $lua = <<<LUA
if redis.call('get',KEYS\[1\]) == ARGV\[1\] then 
   return redis.call('del',KEYS\[1\]) 
else
   return 0 
end
LUA;
            $result = $redis->rawCommand(\['EVAL', $lua, 1, self::KEY_PREFIX . $key, $randNum\]);
            return $result->getData();
        });
    }
    public function lockFunction($key, callable $callback)
    {
        $random = $this->lock($key);
        if ($random !== null) {
            $result = call\_user\_func($callback);
            $this->unlock($key, $random);
            return $result;
        } else {
            return false;
        }
    }
}
目录
相关文章
|
4月前
|
存储 负载均衡 NoSQL
【赵渝强老师】Redis Cluster分布式集群
Redis Cluster是Redis的分布式存储解决方案,通过哈希槽(slot)实现数据分片,支持水平扩展,具备高可用性和负载均衡能力,适用于大规模数据场景。
353 2
|
4月前
|
存储 缓存 NoSQL
【📕分布式锁通关指南 12】源码剖析redisson如何利用Redis数据结构实现Semaphore和CountDownLatch
本文解析 Redisson 如何通过 Redis 实现分布式信号量(RSemaphore)与倒数闩(RCountDownLatch),利用 Lua 脚本与原子操作保障分布式环境下的同步控制,帮助开发者更好地理解其原理与应用。
292 6
|
5月前
|
存储 缓存 NoSQL
Redis核心数据结构与分布式锁实现详解
Redis 是高性能键值数据库,支持多种数据结构,如字符串、列表、集合、哈希、有序集合等,广泛用于缓存、消息队列和实时数据处理。本文详解其核心数据结构及分布式锁实现,帮助开发者提升系统性能与并发控制能力。
|
3月前
|
NoSQL Java 调度
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
分布式锁是分布式系统中用于同步多节点访问共享资源的机制,防止并发操作带来的冲突。本文介绍了基于Spring Boot和Redis实现分布式锁的技术方案,涵盖锁的获取与释放、Redis配置、服务调度及多实例运行等内容,通过Docker Compose搭建环境,验证了锁的有效性与互斥特性。
223 0
分布式锁与分布式锁使用 Redis 和 Spring Boot 进行调度锁(不带 ShedLock)
|
3月前
|
缓存 NoSQL 关系型数据库
Redis缓存和分布式锁
Redis 是一种高性能的键值存储系统,广泛用于缓存、消息队列和内存数据库。其典型应用包括缓解关系型数据库压力,通过缓存热点数据提高查询效率,支持高并发访问。此外,Redis 还可用于实现分布式锁,解决分布式系统中的资源竞争问题。文章还探讨了缓存的更新策略、缓存穿透与雪崩的解决方案,以及 Redlock 算法等关键技术。
|
5月前
|
NoSQL Redis
Lua脚本协助Redis分布式锁实现命令的原子性
利用Lua脚本确保Redis操作的原子性是分布式锁安全性的关键所在,可以大幅减少由于网络分区、客户端故障等导致的锁无法正确释放的情况,从而在分布式系统中保证数据操作的安全性和一致性。在将这些概念应用于生产环境前,建议深入理解Redis事务与Lua脚本的工作原理以及分布式锁的可能问题和解决方案。
213 8
|
6月前
|
缓存 NoSQL 算法
高并发秒杀系统实战(Redis+Lua分布式锁防超卖与库存扣减优化)
秒杀系统面临瞬时高并发、资源竞争和数据一致性挑战。传统方案如数据库锁或应用层锁存在性能瓶颈或分布式问题,而基于Redis的分布式锁与Lua脚本原子操作成为高效解决方案。通过Redis的`SETNX`实现分布式锁,结合Lua脚本完成库存扣减,确保操作原子性并大幅提升性能(QPS从120提升至8,200)。此外,分段库存策略、多级限流及服务降级机制进一步优化系统稳定性。最佳实践包括分层防控、黄金扣减法则与容灾设计,强调根据业务特性灵活组合技术手段以应对高并发场景。
1639 7
|
9月前
|
数据采集 存储 数据可视化
分布式爬虫框架Scrapy-Redis实战指南
本文介绍如何使用Scrapy-Redis构建分布式爬虫系统,采集携程平台上热门城市的酒店价格与评价信息。通过代理IP、Cookie和User-Agent设置规避反爬策略,实现高效数据抓取。结合价格动态趋势分析,助力酒店业优化市场策略、提升服务质量。技术架构涵盖Scrapy-Redis核心调度、代理中间件及数据解析存储,提供完整的技术路线图与代码示例。
901 0
分布式爬虫框架Scrapy-Redis实战指南
|
7月前
|
数据采集 存储 NoSQL
基于Scrapy-Redis的分布式景点数据爬取与热力图生成
基于Scrapy-Redis的分布式景点数据爬取与热力图生成
375 67
|
10月前
|
NoSQL Java 中间件
【📕分布式锁通关指南 02】基于Redis实现的分布式锁
本文介绍了从单机锁到分布式锁的演变,重点探讨了使用Redis实现分布式锁的方法。分布式锁用于控制分布式系统中多个实例对共享资源的同步访问,需满足互斥性、可重入性、锁超时防死锁和锁释放正确防误删等特性。文章通过具体示例展示了如何利用Redis的`setnx`命令实现加锁,并分析了简化版分布式锁存在的问题,如锁超时和误删。为了解决这些问题,文中提出了设置锁过期时间和在解锁前验证持有锁的线程身份的优化方案。最后指出,尽管当前设计已解决部分问题,但仍存在进一步优化的空间,将在后续章节继续探讨。
1282 131
【📕分布式锁通关指南 02】基于Redis实现的分布式锁

热门文章

最新文章