Redis 分布式锁深度解析:如何防止锁被其他协程误删

分布式锁实战:Redis与Redisson的深度解析 Redis原生方案适用于轻量级场景,需关注原子性与锁过期问题Redisson框架通过自动续期、多种锁模式等特性,提供了企业级的分布式锁解决方案在实际应用中,应根据系统规模、一致性要求和业务特性选择合适的实现方式,同时结合监控和报警机制保障系统的稳定性。 阅读详情

在分布式锁的设计中,"互斥性" 保证了同一时间只有一个节点能操作资源,而 "安全性" 则确保了锁不会被无关节点误删。本文将聚焦于 Redis 分布式锁的安全性问题,深入解析如何通过巧妙设计 value 值和原子操作,防止锁被其他协程或节点误删除。

一、为什么会出现锁被误删的情况?

"过期时间可能导致锁被误删",这是分布式锁实现中一个典型的安全隐患。让我们通过一个具体场景理解这个问题:

  1. 客户端 A 获取锁,设置 value 为 "lock-value-A",过期时间 8 秒
  2. 客户端 A 的业务逻辑执行缓慢,8 秒后锁自动过期释放
  3. 客户端 B 此时成功获取锁,设置 value 为 "lock-value-B"
  4. 客户端 A 的业务逻辑终于执行完成,执行DEL命令释放锁
  5. 结果:客户端 A 删除了客户端 B 持有的锁,导致新的客户端 C 也能获取到锁,引发并发问题

问题的核心在于:释放锁时没有验证锁的归属权。如果只是简单执行DEL命令,任何知道锁 key 的客户端都能删除锁,这显然不符合分布式锁的安全要求。

二、解决方案:用唯一 value 标识锁的归属

解决锁误删问题的关键思路是:给每个锁设置唯一的 value 值,释放锁时先验证 value 是否匹配,只有匹配时才删除锁。

1. 唯一 value 的生成策略

value 需要满足 "全局唯一性",确保不同客户端、不同时间获取的锁具有不同标识。常见的生成方式有:

  • UUID/GUID:通用唯一识别码,如uuid.New().String()
  • 客户端 ID + 时间戳:如"client-123:" + time.Now().UnixNano()
  • 随机数 + 进程 ID:如"pid-456:" + rand.Intn(1000000)

在 Go 语言中生成 UUID 的示例:

import (
    "github.com/google/uuid"
)

// 生成唯一锁值
func generateLockValue() string {
    return uuid.New().String()
}

这个 value 会在获取锁时作为SET命令的参数存入 Redis,成为锁的 "身份证"。

2. 带 value 验证的锁释放流程

释放锁时需要执行两个操作:

  1. 获取锁的当前 value
  2. 比较 value 是否与自己持有的一致,一致则删除

但这两个操作如果分开执行,在高并发场景下仍然可能出现问题(如刚获取到 value,锁就过期被其他客户端获取)。因此必须保证这两个操作的原子性。

Redis 的 Lua 脚本功能正好满足这一需求 ——Redis 会将整个 Lua 脚本作为一个原子操作执行,中间不会被其他命令打断。

释放锁的 Lua 脚本如下:

-- 释放锁的Lua脚本
-- KEYS[1]:锁的key
-- ARGV[1]:当前客户端持有的value
if redis.call('GET', KEYS[1]) == ARGV[1] then
    -- value匹配,删除锁
    return redis.call('DEL', KEYS[1])
else
    -- value不匹配,不做操作
    return 0
end

这个脚本的逻辑很清晰:只有当 Redis 中锁的当前值与客户端持有的值完全一致时,才会执行删除操作,否则直接返回 0(不做任何处理)。

三、完整实现:从获取到释放的全流程

结合前面的知识,我们可以实现一个安全的分布式锁,包含完整的 value 验证机制。

1. Go 语言实现示例

package main

import (
	"context"
	"time"

	"github.com/go-redis/redis/v8"
	"github.com/google/uuid"
)

type RedisLock struct {
	client     *redis.Client
	lockKey    string       // 锁的key
	lockValue  string       // 锁的唯一value
	expireTime time.Duration // 过期时间
}

// 创建新锁实例
func NewRedisLock(client *redis.Client, key string, expireTime time.Duration) *RedisLock {
	return &RedisLock{
		client:    client,
		lockKey:   key,
		expireTime: expireTime,
	}
}

// 获取锁
func (rl *RedisLock) Lock(ctx context.Context) (bool, error) {
	// 生成唯一value
	rl.lockValue = uuid.New().String()
	
	// 执行SET NX命令,同时设置过期时间
	result, err := rl.client.SetNX(
		ctx,
		rl.lockKey,
		rl.lockValue,
		rl.expireTime,
	).Result()
	
	return result, err
}

// 释放锁(带value验证)
func (rl *RedisLock) Unlock(ctx context.Context) (bool, error) {
	// 释放锁的Lua脚本
	script := `
		if redis.call('GET', KEYS[1]) == ARGV[1] then
			return redis.call('DEL', KEYS[1])
		else
			return 0
		end
	`
	
	// 执行Lua脚本
	result, err := rl.client.Eval(
		ctx,
		script,
		[]string{rl.lockKey}, // KEYS参数
		rl.lockValue          // ARGV参数
	).Int64()
	
	if err != nil {
		return false, err
	}
	
	// 返回是否成功释放锁
	return result == 1, nil
}

// 获取当前锁的value(用于调试)
func (rl *RedisLock) GetCurrentValue(ctx context.Context) (string, error) {
	return rl.client.Get(ctx, rl.lockKey).Result()
}

2. 关键实现解析

  • Lock 方法:获取锁时生成唯一 value,并通过SetNX命令原子性地设置 key、value 和过期时间
  • Unlock 方法:使用 Lua 脚本实现 "验证 value + 删除锁" 的原子操作,确保只有锁的持有者才能释放锁
  • value 存储:将 value 保存在 RedisLock 实例中,用于释放锁时的验证

这种实现彻底解决了 "锁被误删" 的问题:即使锁过期后被其他客户端获取,原客户端在释放时会发现 value 不匹配,从而放弃删除操作。

四、进阶思考:value 设计的额外用途

除了防止误删,锁的 value 还可以承载更多信息,提升分布式锁的可观测性和功能性:

1. 携带持有者信息

将客户端 ID、进程 ID 等信息编码到 value 中,便于问题排查:

func generateLockValue(clientID string) string {
    // 格式:客户端ID:进程ID:UUID
    return fmt.Sprintf("%s:%d:%s", clientID, os.Getpid(), uuid.New().String())
}

当出现锁异常时,可以通过GET lockKey命令查看当前持有者信息,快速定位问题节点。

2. 支持可重入锁

可重入锁允许同一客户端多次获取同一把锁(类似 Java 的 ReentrantLock)。实现方式是在 value 中记录获取次数:

// 可重入锁的value格式:"clientID:count"
func (rl *ReentrantLock) Lock(ctx context.Context) (bool, error) {
    // 尝试获取锁
    currentValue, err := rl.client.Get(ctx, rl.lockKey).Result()
    
    if err == redis.Nil {
        // 锁不存在,第一次获取
        return rl.client.SetNX(ctx, rl.lockKey, rl.baseValue+":1", rl.expireTime).Result()
    }
    
    // 检查是否是当前客户端持有锁
    if strings.HasPrefix(currentValue, rl.baseValue) {
        // 解析当前计数并加1
        count, _ := strconv.Atoi(strings.Split(currentValue, ":")[1])
        newCount := count + 1
        // 更新计数(需要保证原子性,可使用Lua脚本)
        return true, rl.client.Set(ctx, rl.lockKey, fmt.Sprintf("%s:%d", rl.baseValue, newCount), rl.expireTime).Err()
    }
    
    // 其他客户端持有锁,获取失败
    return false, nil
}

释放时则递减计数,当计数为 0 时才真正删除锁。

五、总结:分布式锁安全的核心原则

防止锁被其他协程误删,本质上是解决 "锁的归属权验证" 问题。总结几个关键原则:

  1. 唯一标识原则:每个锁必须有唯一的 value,作为持有者的身份凭证
  2. 原子操作原则:释放锁时的 "验证 + 删除" 必须是原子操作,Lua 脚本是最佳选择
  3. 最小权限原则:只有锁的持有者才能释放锁,任何情况下都不允许越权操作

在实际开发中,建议直接使用经过验证的开源库(如 Redisson、go-redsync),它们已经妥善处理了这些安全细节。理解这些原理的价值在于:当遇到锁相关的异常时,能够快速定位问题根源,而不是停留在 "API 调用" 的层面。

分布式锁的安全性是一个细节决定成败的领域,一个小小的疏漏就可能导致整个分布式系统的并发控制失效。通过本文的学习,希望你能深刻理解 value 验证机制的重要性,在设计和使用分布式锁时始终保持对 "安全性" 的敬畏。

后端领域的分布式锁:Redis 实现方案 分布式锁需满足以下四大核心属性互斥性:同一时间只有一个客户端持有锁;防止死锁:客户端崩溃后,锁需自动释放(如过期时间);高可用性:锁服务需容忍节点故障(如Redis集群);性能:获取/释放锁的延迟低(如Redis的亚毫秒级响应)。可选属性公平性:按请求顺序分配锁(避免饥饿);可重入性:同一客户端可重复获取已持有的锁;容错性:部分节点故障不影响锁服务。适用场景:高并发、弱一致的场景(如电商库存、分布式任务);最佳实践。 阅读详情

相关推荐

Redis分布式锁的终极指南:从原理到实战的完整解析

Redis分布式锁是现代高并发系统的关键组件,它能有效解决资源竞争和数据一致性问题。其核心特性包括互斥性(通过原子操作SET key value NX EX实现)、超时释放(防止死锁)、可重入性(支持同一线程多次加锁)和自动续期(Redisson的看门狗机制)。实现时需注意原子性操作、锁安全释放(验证唯一标识)及性能优化。解决方案涵盖Redisson客户端、Lua脚本和RedLock算法(用于集群环境),确保系统在高并发场景下的可靠性与一致性。

墨夶的博客 1982

解决Redis分布式锁误删问题

解决Redis分布式锁误删问题

m0_61787196的博客 784

别再硬编码了!WinCC子画面动态加载的5种C脚本写法对比

本文深入解析WinCC子画面动态加载的5种C脚本实现方案,包括基础加载、路径动态解析、多语言切换、窗口自适应布局和混合架构交互。通过代码示例和场景分析,帮助开发者避免硬编码问题,提升工业自动化系统的交互效率和可维护性。特别推荐路径动态解析方案,有效解决项目路径管理的痛点。

weixin_29008691的博客 475

分布式锁解决因为删锁而产生的两种问题

设置锁过期时间,访问数据库后删除锁。涉及的两种问题都是毫秒级别的问题,为了减轻服务器的压力,一般在大厂里要完善的代码。 而小公司里,则不需要这么麻烦,不设计删除锁,直接等待过期时间过去自动删除锁。这样就省去了因为删除锁产生的一系列问题。 分布式锁的两种问题: 第一种问题:锁过期,删除锁,把别人的删了 第1条线程拿锁进去访问数据库,突然该线程发生了意外,等待时间较长,直到过期时间到了(锁释放,第2条线程拿锁进入访问)!突然:第1条线程复活,访问数据库出来,删除锁(此时把第2条线程的锁给删除了) 请设计方案解决

Hiber12的博客 1415

为何LPDDR4支持on-die ECC,但是DDR4却不支持呢?

DRAM的ECC,目前的编码方式都是128bit data,产生8bit parity或者ECC code. 而且必须要128bit才行。这8bit的ECC code是要被写入到DRAM内存里面的,从而read的时候,可以读取这8bit ECC code来判定read data是否有错,如果是SBF,就可以纠错了,如果是double bit,大部分时候是可以detect到错误,即所谓的SEC(Single Error Correct) + DED(Double Error Detect).

一个DRAM从业者的告白 1553

免费 AIS船舶轨迹预测 数据集 分享 (可用于长周期轨迹预测)

论文中使用的数据由提供。有关预处理步骤的详细信息,请参阅。

qq_43747519的博客 1万+

Redis分布式锁最简单的实现SET lock 1 EX 10 NX。如何避免死锁?锁被别人释放怎么办?

想要实现分布式锁,必须要求 Redis 有「互斥」的能力,我们可以使用 SETNX 命令,这个命令表示SET if Not Exists,即如果 key 不存在,才会设置它的值,否则什么也不做。上面的命令执行时,每个客户端在释放锁时,都是「无脑」操作,并没有检查这把锁是否还「归自己持有」,所以就会发生释放别人锁的风险,这样的解锁流程,很不「严谨」!如何解决这个问题呢?此时,加锁成功的客户端,就可以去操作「共享资源」,例如,修改 MySQL 的某一行数据,或者调用一个 API 请求。怎么解决这个问题呢?

学亮编程手记 789

FC游戏Hack实战:从零开始修改游戏数据

本文以《忍者神龟3》为例,详细介绍了从零开始进行FC游戏Hack的完整实战流程。通过使用FCEUX模拟器和调试器,结合“递减法”精准定位生命值内存地址,并利用十六进制编辑器修改关键指令,最终实现“无限生命”效果。文章旨在引导读者理解游戏数据修改的核心原理与操作技巧,重温经典的同时深入计算机程序运行机制。

weixin_28366353的博客 356

redis中的锁,通过UUID防止误删

redis中的锁,通过UUID防止误删

荣山的博客 595

基于eBPF的零侵入LLM智能体观测:AgentSight架构与实战

在分布式系统和云原生架构中,可观测性是保障应用稳定运行与高效排障的核心能力。其原理在于通过日志、指标和追踪三大支柱,从外部视角持续收集和分析系统状态,而非依赖应用内部埋点。这一技术价值在于能以统一、低开销的方式,理解复杂系统的内部行为,尤其在微服务、容器化及Serverless等动态环境中至关重要。应用场景广泛覆盖了性能监控、安全审计、故障诊断与根因分析。当我们将这一基础理念应用于当下快速发展的AI智能体领域时,便自然引出了对LLM智能体进行无侵入、系统级行为观测的需求。传统的应用层方案需要集成SDK,对闭

weixin_28696185的博客 322

FPGA开发中的操作系统

在FPGA开发中,操作系统起着关键的作用,它负责任务管理、资源管理、中断处理和设备驱动程序等功能。在实际的FPGA开发中,可以选择使用现有的操作系统,如MicroC/OS-II、FreeRTOS等,或者根据具体需求自行设计和开发操作系统。在FPGA(可编程逻辑门阵列)开发中,操作系统扮演着关键的角色,为硬件设计师提供了一种有效的方式来管理资源、调度任务并实现系统级功能。本文将介绍FPGA开发中的操作系统概念,并提供一个简单的示例代码来说明如何在FPGA平台上实现一个基本的操作系统。

CqpFsharp的博客 1155

【Redis】分布式锁,实现分布式锁,设置过期时间,UUID防误删,LUA脚本防误删

@GetMapping("testLock") public void testLock(){ //1获取锁,setne Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "111",3,TimeUnit.SECONDS); //2获取锁成功、查询num的值 if(lock){ Object value = redisTemplate.opsForValue().get("...

Java—wang的博客 765

大数据领域存算分离:教育行业的数据应用探索

在当今数字化时代,教育行业积累了海量的数据,如学生的学习记录、考试成绩、在线学习行为等。如何高效地存储、管理和分析这些数据,以挖掘其潜在价值,为教育决策、教学改进和学生个性化发展提供支持,成为教育行业面临的重要挑战。大数据领域的存算分离技术为解决这一问题提供了新的思路和方法。本文旨在探索存算分离技术在教育行业的数据应用,分析其原理、优势和实际应用场景,为教育行业的数据管理和分析提供参考。本文共分为十个部分。第一部分介绍背景,包括目的和范围、预期读者、文档结构概述和术语表;

AGI×大数据,开启智能时代的认知跃迁;解码AGI,赋能数据驱动的智能革命。 812

Redis分布式锁深度剖析:从原理到高可用实践

Redis分布式锁通过SET key random_value EX time NX实现原子加锁,配合Lua脚本保障安全释放。关键挑战在于锁续期和集群高可用——看门狗机制解决业务执行超时问题,RedLock算法应对主从切换场景。工业级实现首选Redisson,提供可重入锁、公平锁等高级特性。选型需权衡CAP理论:Redis满足AP高可用,Zookeeper/etcd提供CP强一致性。未来趋势聚焦Serverless适配和跨云锁服务,但核心仍是平衡互斥性、容错性与时效性。

JouJz的博客 2万+

RK3568/RK3588 + YT 9215交换机芯片,MAC TO MAC 调试记录

原来的方案是rk3568 gmac 直接接phy,phy 接 switch 芯片,只是把交换芯片当交换用,驱动方面基本不用开发,但是要做vlan 那么必须涉及交换芯片的开发。选择裕太微有两个方面的原因:1.国产化替代2.可获得原厂技术支持3.目前已经完成 两个gmac 口交换芯片的配置,实现vlan,内部交换,两个光口可正常通信。

Nyiragongo的博客 1万+

VB摄像头监控

该VB模块可对摄像头捕获的画面进行录像等,可在创建的窗口进行控制

Redis 分布式锁深度解析:从原理到实战,一篇全懂!

Redis分布式锁核心点│ SET key value NX EX seconds│←原子加锁│ if GET == value => DEL key │←Lua原子释放锁│ value = UUID() 唯一标识 ││ 设置 TTL 避免死锁 │↑ ↑单机适用 RedLock:多节点安全原子加锁 + 过期时间 + 身份校验 + 安全释放,RedLock 是其在分布式环境下的延伸方案。

一键难忘的博客 3122

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

​​​​Redis 分布式锁深度解析:从原理到实战的互斥保障机制

互斥性SET NX命令确保同一时间只有一个节点能获取锁;安全性:随机 value+Lua 脚本确保锁只能被持有者释放,避免误删;避免死锁:过期时间机制确保锁最终会被释放。Redis 分布式锁通过SET NX互斥性SET NX命令确保同一时间只有一个节点能获取锁;安全性:随机 value+Lua 脚本确保锁只能被持有者释放,避免误删;避免死锁:过期时间机制确保锁最终会被释放;高性能:针对不同资源(如不同商品)创建独立锁,减少锁竞争。

Landcc的博客 1364

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

分布式锁深度解析:从原理到实战

本文系统解析了分布式锁的核心原理、主流方案及生产实践,Redis 方案因其高性能与易实现性成为首选,而 Zookeeper 方案则适用于强一致性场景。在实际应用中,需根据业务特性设计锁粒度、超时时间与监控体系,并通过幂等性设计与故障演练确保锁服务的可靠性。云原生锁服务:Kubernetes 原生锁控制器(如基于 etcd 的租约机制)无锁化编程:通过事务性内存(Transactional Memory)或乐观锁替代显式锁量子加密锁:利用量子密钥分发(QKD)提升锁的安全性(理论探索阶段)

無言的博客 1264

Redis分布式锁从入门到精通:从SETNX到Redisson看门狗机制

方案优点缺点适用场景SETNX基础版实现简单无续期、不可重入学习测试手动Lua脚本原子操作功能单一简单场景Redisson功能完善、自动续期、可重入引入依赖生产环境首选。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 1513

Redis分布式锁深度解析:从原理到实践

分布式锁是分布式系统中协调多节点操作的核心机制,本文通过原理剖析、代码实现和方案对比,全面解析Redis分布式锁的实现方案及生产环境中的最佳实践。

tr990511的专栏 1205

从Redisson源码角度深入理解Redis分布式锁的正确实现

分布式锁实现与优化 本文介绍了分布式锁的必要性及其实现方案。在集群环境下,传统的synchronized无法跨进程同步,因此需要引入分布式锁来解决并发问题。文章通过商品库存扣减案例,逐步分析并优化分布式锁的实现: 基础实现:使用Redis的setnx命令实现简单分布式锁,但存在死锁风险 优化方案:添加锁过期时间并确保原子性操作,通过UUID标识解决误删问题 高级方案:引入Redisson的看门狗机制实现自动续命,确保业务执行期间锁不会提前释放 文章还探讨了分布式锁实现中的关键问题,如原子性操作、锁续命机制等

m0_60963435的博客 743

redis:Redisson分布式锁深度解析

这篇博客系统解析了Redisson分布式锁的核心内容。你若对源码解析的深度、案例场景的复杂度或面试追问的解决方案有调整需求,欢迎随时告知。

weixin_43290370的博客 991

【 Redis八股:分布式锁深度解析:从实现原理到生产实践】

在分布式系统中,多个进程或线程可能需要同时访问共享资源。为了避免竞争条件和数据不一致的问题,分布式锁成为了一个重要的协调机制。Redis作为一种高性能的内存数据库,常被用来实现分布式锁。

m0_64006292的博客 1066

Redis分布式锁深度解析:从setnx到Redisson看门狗及实战避坑

分布式锁是分布式系统中协调并发访问共享资源的基础机制,其核心目标是实现跨进程的互斥、安全与可用。从单机锁到多实例部署,锁的语义必须从JVM内部提升到外部存储,Redis凭借单线程命令模型成为主流选择。早期基于setnx的加锁方式受限于非原子操作,存在崩溃后死锁的隐患;而SET NX EX原子命令的引入,合并了加锁与过期时间设置,奠定了Redis分布式锁的基石。进一步地,Redisson通过看门狗机制动态续期,解决了锁过期与业务执行超时的竞态,并提供了可重入锁、公平锁等高级能力。在秒杀、定时任务、接口幂等等高

weixin_33146151的博客 348

【Redis】分布式锁深度解析:实现、可重入、主从一致性与强一致方案

Redis 分布式锁深度解析:实现、可重入、主从一致性与强一致方案

金哥的博客 663

Java面试-Redis 实现分布式锁的正确姿势:SETNX + EXPIRE + Lua

《Java面试200问》系列最新文章深入解析Redis分布式锁的实现方案,从基础SETNX到原子性SET命令,再到结合UUID和Lua脚本的完整解决方案。文章通过5个演进阶段详细剖析技术原理,指出常见陷阱,并提供Spring Boot实战代码。核心内容涵盖锁过期处理、主从切换问题及可重入锁实现,最后附上面试高频问题集锦,帮助开发者从入门到精通掌握分布式锁技术。全文2000+字,图文并茂,是准备Java技术面试的实用指南。

千淘万漉虽辛苦,吹尽狂沙始到金 2万+
上一篇: Redis 分布式锁深度解析:过期时间与自动续期机制
下一篇: Redis 分布式锁在集群环境中的挑战与 RedLock 解决方案
Tech小七
博客等级 码龄3年 1571粉丝 196原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值