百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

Redis实现分布式锁实现(redis分布式锁实现Java)

mhr18 2025-03-28 18:33 27 浏览 0 评论

在分布式系统中,多个服务实例常并行操作数据库、缓存等共享资源,若缺乏协调机制,易致数据不一致,如超卖、重复扣款等问题。为此,分布式锁发挥关键作用,并需满足以下核心特性:互斥性,即同一时刻仅一个客户端能持有锁,确保共享资源操作安全唯一;容错性,即便部分节点故障,锁服务也能如 RedLock 算法般保持可用,维持系统稳定;自动过期避免死锁,通过设置 TTL,客户端崩溃时自动释放锁,防止死锁;可重入性,同一客户端持有锁期间可多次获取,且锁服务需记录持有者身份,避免多次获取导致的异常。

1.Redis实现分布式锁的核心原理

Redis的单线程特性保证命令执行的原子性,核心命令包括:

  • SETNX key value :仅当键不存在时设置值,用于抢占锁。
  • EXPIRE key ttl :设置锁的过期时间,防止死锁。
  • DEL key :释放锁,但需校验锁的归属。

问题与改进

  • 原子性缺陷 :早期方案需先执行SETNX再执行EXPIRE,若两者之间客户端崩溃,锁将永不过期。Redis 2.6+支持通过SET key value NX PX ttl单命令解决
  • 唯一标识 value需为客户端唯一ID(如UUID+线程ID),避免误删其他客户端的锁

基础实现与代码示例:

1. 获取锁(Java + Jedis)

public boolean tryLock(String lockKey, String clientId, int expireTime) {
    Jedis jedis = jedisPool.getResource();
    try {
        // 使用SET命令的NX和PX参数保证原子性,需根据业务执行时间动态调整,避免过早过期或长时间占用
        String result = jedis.set(lockKey, clientId, 
            SetParams.setParams().nx().px(expireTime));
        return "OK".equals(result);
    } finally {
        jedis.close();
    }
}

2.释放锁(Lua脚本)

Lua脚本优势 :将GETDEL合并为原子操作,避免竞态条件。

public boolean releaseLock(String lockKey, String clientId) {
    Jedis jedis = jedisPool.getResource();
    try {
        String script = "if redis.call('GET', KEYS[1]) == ARGV[1] then " +
                        "return redis.call('DEL', KEYS[1]) " +
                        "else return 0 end";
        Object result = jedis.eval(script, 
            Collections.singletonList(lockKey), 
            Collections.singletonList(clientId));
        return Long.valueOf(1).equals(result);
    } finally {
        jedis.close();
    }
}

2.Redis分布式锁高级优化与实战技巧


2.1.RedLock算法

RedLock 是由 Redis 创始人提出的一种分布式锁算法,旨在解决 Redis 主从复制环境下的容错性问题。与传统的单机锁不同,RedLock 在多个 Redis 实例间分配锁,确保在部分节点故障的情况下,系统仍能保证锁的有效性。

其核心特征包括:

  • 算法概述:RedLock 将锁请求分别发送到多个 Redis 实例,并设定严格的超时机制。只有当多数节点都成功获取锁时,客户端才视为获得了锁。
  • 容错性:RedLock 能够保证在某些节点发生故障时,其他节点仍可继续提供服务,避免单点故障。
def redlock_acquire(nodes, lock_key, ttl):
    start_time = now()
    acquired_nodes = []
    for node in nodes:
        if node.set(lock_key, client_id, nx=True, px=ttl):
            acquired_nodes.append(node)
    elapsed = now() - start_time
    if len(acquired_nodes) >= len(nodes)/2 + 1 and elapsed < ttl:
        return True
    else:
        # 释放已获取的锁
        for node in acquired_nodes:
            node.delete(lock_key)
        return False

2.2.可重入锁实现

在分布式系统里,同一客户端在进行递归调用等操作时,往往需要多次获取同一把锁。为达成这一需求,可采用如下方案:

  • value中存储clientId:重入次数(如uuid:threadId:2)。
  • 每次获取锁时,若clientId匹配,增加重入次数。
  • 释放锁时,重入次数减至0时删除锁。
// 获取锁
function acquire(lock, client):
    value = get(lock)
    if value is null:
        set(lock, client + ":1", expire)
        return true
    storedClient, count = split(value, ":")
    if storedClient == client:
        set(lock, client + ":" + (count + 1), expire)
        return true
    return false

// 释放锁
function release(lock, client):
    value = get(lock)
    if value is not null:
        storedClient, count = split(value, ":")
        if storedClient == client:
            if count - 1 == 0:
                delete(lock)
            else:
                set(lock, client + ":" + (count - 1))
            return true
    return false

// 示例使用
lock = "myLock"
client = generateClientID()
if acquire(lock, client):
    try:
        // 业务操作
        acquire(lock, client)
    finally:
        release(lock, client)

3.生产级实践建议


选择成熟的框架:

  • Redisson :封装了RedLock、看门狗、可重入锁等高级功能,简化开发。
  • Spring Integration :提供RedisLockRegistry,支持声明式锁管理。

性能优化:

  • 减少锁粒度 :按业务维度拆分锁(如user_lock_{userId}而非全局锁)。
  • 异步释放锁 :通过异步线程池执行非核心操作,缩短锁持有时间。

监控与告警:

  • 监控锁等待时间、获取失败率,及时发现性能瓶颈。
  • 记录锁操作日志,便于排查分布式死锁问题。

4.总结

Redis分布式锁通过原子操作、唯一标识、Lua脚本等机制,实现了高效可靠的资源互斥访问。实际应用中需结合业务场景选择锁粒度、优化性能,并通过成熟框架(如Redisson)降低开发复杂度。未来,随着分布式系统的复杂化,动态锁续期、多维度监控等技术将成为关键优化方向。

相关推荐

Redis合集-使用benchmark性能测试

采用开源Redis的redis-benchmark工具进行压测,它是Redis官方的性能测试工具,可以有效地测试Redis服务的性能。本次测试使用Redis官方最新的代码进行编译,详情请参见Redis...

Java简历总被已读不回?面试挂到怀疑人生?这几点你可能真没做好

最近看了几十份简历,发现大部分人不是技术差,而是不会“卖自己”——一、简历死穴:你写的不是经验,是岗位说明书!反面教材:ד使用SpringBoot开发项目”ד负责用户模块功能实现”救命写法:...

redission YYDS(redission官网)

每天分享一个架构知识Redission是一个基于Redis的分布式Java锁框架,它提供了各种锁实现,包括可重入锁、公平锁、读写锁等。使用Redission可以方便地实现分布式锁。red...

从数据库行锁到分布式事务:电商库存防超卖的九重劫难与破局之道

2023年6月18日我们维护的电商平台在零点刚过3秒就遭遇了严重事故。监控大屏显示某爆款手机SKU_IPHONE13_PRO_MAX在库存仅剩500台时,订单系统却产生了1200笔有效订单。事故复盘发...

SpringBoot系列——实战11:接口幂等性的形而上思...

欢迎关注、点赞、收藏。幂等性不仅是一种技术需求,更是数字文明对确定性追求的体现。在充满不确定性的网络世界中,它为我们建立起可依赖的存在秩序,这或许正是技术哲学最深刻的价值所在。幂等性的本质困境在支付系...

如何优化系统架构设计缓解流量压力提升并发性能?Java实战分享

如何优化系统架构设计缓解流量压力提升并发性能?Java实战分享在高流量场景下。首先,我需要回忆一下常见的优化策略,比如负载均衡、缓存、数据库优化、微服务拆分这些。不过,可能还需要考虑用户的具体情况,比...

Java面试题: 项目开发中的有哪些成长?该如何回答

在Java面试中,当被问到“项目中的成长点”时,面试官不仅想了解你的技术能力,更希望看到你的问题解决能力、学习迭代意识以及对项目的深度思考。以下是回答的策略和示例,帮助你清晰、有说服力地展示成长点:一...

互联网大厂后端必看!Spring Boot 如何实现高并发抢券逻辑?

你有没有遇到过这样的情况?在电商大促时,系统上线了抢券活动,结果活动刚一开始,服务器就不堪重负,出现超卖、系统崩溃等问题。又或者用户疯狂点击抢券按钮,最后却被告知无券可抢,体验极差。作为互联网大厂的后...

每日一题 |10W QPS高并发限流方案设计(含真实代码)

面试场景还原面试官:“如果系统要承载10WQPS的高并发流量,你会如何设计限流方案?”你:“(稳住,我要从限流算法到分布式架构全盘分析)…”一、为什么需要限流?核心矛盾:系统资源(CPU/内存/数据...

Java面试题:服务雪崩如何解决?90%人栽了

服务雪崩是指微服务架构中,由于某个服务出现故障,导致故障在服务之间不断传递和扩散,最终造成整个系统崩溃的现象。以下是一些解决服务雪崩问题的常见方法:限流限制请求速率:通过限流算法(如令牌桶算法、漏桶算...

面试题官:高并发经验有吗,并发量多少,如何回复?

一、有实际高并发经验(建议结构)直接量化"在XX项目中,系统日活用户约XX万,核心接口峰值QPS达到XX,TPS处理能力为XX/秒。通过压力测试验证过XX并发线程下的稳定性。"技术方案...

瞬时流量高并发“保命指南”:这样做系统稳如泰山,老板跪求加薪

“系统崩了,用户骂了,年终奖飞了!”——这是多少程序员在瞬时大流量下的真实噩梦?双11秒杀、春运抢票、直播带货……每秒百万请求的冲击,你的代码扛得住吗?2025年了,为什么你的系统一遇高并发就“躺平”...

其实很多Java工程师不是能力不够,是没找到展示自己的正确姿势。

其实很多Java工程师不是能力不够,是没找到展示自己的正确姿势。比如上周有个小伙伴找我,五年经验但简历全是'参与系统设计''优化接口性能'这种空话。我就问他:你做的秒杀...

PHP技能评测(php等级考试)

公司出了一些自我评测的PHP题目,现将题目和答案记录于此,以方便记忆。1.魔术函数有哪些,分别在什么时候调用?__construct(),类的构造函数__destruct(),类的析构函数__cal...

你的简历在HR眼里是青铜还是王者?

你的简历在HR眼里是青铜还是王者?兄弟,简历投了100份没反应?面试总在第三轮被刷?别急着怀疑人生,你可能只是踩了这些"隐形求职雷"。帮3630+程序员改简历+面试指导和处理空窗期时间...

取消回复欢迎 发表评论: