当前位置: 首页 > article >正文

【Golang学习之旅】如何在Go语言中使用Redis实现分布式锁,并解决锁过期导致的并发问题?

文章目录

    • 前言
    • 1. 分布式锁的基本原理
      • 1.1 锁过期导致并发问题的解决:
    • 2. Go实现分布式锁
    • 3. 代码实现分布式锁
      • 3.1 安装Go-Redis包
      • 3.2 创建分布式锁工具函数
      • 3.3 代码解析
    • 4. 如何避免锁过期导致的并发问题
      • 4.1 延长锁的有效期
      • 4.2 利用Redis的`WATCH`命令(乐观锁)
    • 5. 解决死锁问题:RedLock算法
    • 6. 结论

前言

在Go语言中使用Redis实现分布式锁并解决锁过期导致的并发问题是一个常见的需求,尤其是在需要确保分布式环境中多个实例不会同时操作同一资源时。Redis提供了强大的原子操作,能够帮助我们实现可靠的分布式锁。

1. 分布式锁的基本原理

分布式锁的基本思想是:通过在Redis中设置一个键(key),并为该键设置一个过期时间(expire time)。只有第一个获取到该锁的客户端才能成功操作资源,其他客户端则需要等待或失败,避免了并发竞争问题。

步骤:

  1. 客户端请求锁:客户端通过SETNX命令(或SET命令带NX PX参数)设置一个唯一的锁键值对。
  2. 成功获取锁:如果Redis成功设置了锁(键不存在),则表示客户端成功获得了锁。
  3. 释放锁:操作完成后,客户端需要显式地释放锁。

1.1 锁过期导致并发问题的解决:

有时,锁可能会因为过期而自动释放,这会导致多个客户端竞争同一资源。为了解决这个问题,可以通过两种方式来增强锁的可靠性:

  • 加长锁的过期时间:为锁设置较长的过期时间,确保在业务逻辑执行期间,锁不会过期。
  • 使用Redis的Watch命令:在操作过程中,避免直接设置过期时间,而是通过Watch来确保在整个操作过程中锁不会丢失。
  • 使用Redis的RedLock算法:RedLock是Redis官方推荐的分布式锁方案,通过在多个独立的Redis实例中设置锁,提高了锁的可用性和容错性。

2. Go实现分布式锁

我们可以使用Go语言结合Redis客户端(如github.com/go-redis/redis/v8)来实现分布式锁。以下是一个使用Redis实现简单分布式锁的例子。

3. 代码实现分布式锁

3.1 安装Go-Redis包

首先,需要安装Redis的Go客户端:

go get github.com/go-redis/redis/v8

3.2 创建分布式锁工具函数

package mainimport ("context""fmt""log""time""github.com/go-redis/redis/v8"
)var (rdb     *redis.Clientctx     = context.Background()lockKey = "mylock" // 锁的键
)func initRedis() {rdb = redis.NewClient(&redis.Options{Addr:     "localhost:6379",Password: "", // no password setDB:       0,  // use default DB})
}func acquireLock() (bool, error) {// 使用 SETNX 命令加锁,锁成功返回trueresult,err := rdb.SetNX(ctx, lockKey, "locked", 100*time.Second).Result() // 锁的过期时间为10秒if err != nil {return false, err}return result, nil
}func performTask() {// 模拟任务处理fmt.Println("Task is being performed...")time.Sleep(5 * time.Second)fmt.Println("Task is completed.")
}func releaseLock() error {// 删除锁,释放资源_, err := rdb.Del(ctx, lockKey).Result()return err
}func main() {initRedis()defer rdb.Close()// 获取锁locked, err := acquireLock()if err != nil {log.Fatalf("获取锁失败: %v", err)return}if !locked {log.Println("锁已被其他实例占用,稍后重试...")return}log.Println("成功获取锁,开始执行任务")performTask()  // 执行任务// 释放锁if err := releaseLock(); err != nil {log.Fatalf("释放锁失败: %v", err)}log.Println("任务执行完毕,锁已释放")
}

3.3 代码解析

  1. 获取锁
    • SETNX是Redis的一个原子操作,意思是“仅在键不存在时设置键”。这里我们使用SETNX来尝试获取锁。
    • 如果锁获取成功,我们会设置一个10秒的过期时间,确保即使任务异常,锁也能在指定时间后自动释放。
  2. 释放锁
    • 当任务完成时,调用Del命令删除锁,从而释放资源。
  3. 加锁失败处理
    • 如果锁已经被其他实例持有,当前实例会输出提示信息并结束程序(可以根据实际情况选择重试机制)。

4. 如何避免锁过期导致的并发问题

4.1 延长锁的有效期

为了防止在长时间的任务执行过程中锁被Redis自动删除,可以在任务执行过程中动态扩展锁的过期时间。

func extendLockExpire() error {// 每5秒延长一次锁的过期时间_, err := rdb.Expire(ctx, lockKey, 10*time.Second).Result()return err
}

4.2 利用Redis的WATCH命令(乐观锁)

WATCH命令可以监视某些键,如果在事务执行期间这些键的值被改变,那么事务会失败。可以使用WATCH命令来确保锁的过程中不会被其他线程修改。

func watchAndAcquireLock() (bool, error) {// 监视锁键rdb.Watch(ctx, func(tx *redis.Tx) error {// 在事务内进行获取锁的操作result, err := tx.SetNX(ctx, lockKey, "locked", 10*time.Second).Result()if err != nil {return err}if !result {return fmt.Errorf("锁已被其他实例占用")}return nil}, lockKey)return true, nil
}

5. 解决死锁问题:RedLock算法

Redis 官方推荐使用 RedLock 算法来保证在多个 Redis 实例上的高可靠性和容错性。RedLock 通过多个 Redis 实例(通常是 5 个)来保证锁的可靠性。

在 RedLock 中,多个 Redis 实例必须同意锁的存在,只有当所有 Redis 实例返回成功时,才能确认锁的成功获取。RedLock 解决了单个 Redis 实例宕机导致锁无法释放的问题。

你可以使用一些第三方库来实现 RedLock,如 github.com/go-redis/redis/v8的扩展或者其他的 Redis 客户端库。

6. 结论

使用 Redis 实现分布式锁是一个有效的解决方案,可以确保分布式环境中的数据一致性。在 Go 语言中,你可以通过 go-redis 库简单地实现这种机制。为避免锁过期导致并发问题,延长锁的有效期或使用 WATCH 命令可以进一步增强锁的可靠性。

如果你的应用需要跨多个 Redis 实例确保高可用性,建议使用 RedLock 算法,它能在多个 Redis 实例中提供更加健壮的分布式锁。

分布式锁虽然强大,但也有性能开销,因此在实际应用中应根据需求选择合适的锁策略,避免对系统性能的过度影响。

相关文章:

【Golang学习之旅】如何在Go语言中使用Redis实现分布式锁,并解决锁过期导致的并发问题?

文章目录 前言1. 分布式锁的基本原理1.1 锁过期导致并发问题的解决: 2. Go实现分布式锁3. 代码实现分布式锁3.1 安装Go-Redis包3.2 创建分布式锁工具函数3.3 代码解析 4. 如何避免锁过期导致的并发问题4.1 延长锁的有效期4.2 利用Redis的WATCH命令(乐观锁…...

Vue前端开发-Vant组件之Button组件

Vant 有丰富的UI组件,而基础组件是全部组件的核心,基础组件中将常用的元素做了二次的开发,封装成Vant格式组件,如按钮、图片和布局等,这些封装后的Vant组件,提供了更多面向实际应用的属性和事件&#xff0c…...

DeepSeek 接入PyCharm实现AI编程!(支持本地部署DeepSeek及官方DeepSeek接入)

前言 在当今数字化时代,AI编程助手已成为提升开发效率的利器。DeepSeek作为一款强大的AI模型,凭借其出色的性能和开源免费的优势,成为许多开发者的首选。今天,就让我们一起探索如何将DeepSeek接入PyCharm,实现高效、智…...

【Linux网络编程】应用层协议HTTP(请求方法,状态码,重定向,cookie,session)

🎁个人主页:我们的五年 🔍系列专栏:Linux网络编程 🌷追光的人,终会万丈光芒 🎉欢迎大家点赞👍评论📝收藏⭐文章 ​ Linux网络编程笔记: https://blog.cs…...

健康养生:从生活细节开启活力之旅

在忙碌的现代生活里,健康养生不再是一个抽象概念,而是关乎生活质量的关键。其实,只要掌握日常养生要点,就能轻松开启活力满满的健康生活。 饮食是健康的基石。每日饮食需遵循 “彩虹原则”,摄入多种颜色食物。早餐时&…...

DeepSeek + Mermaid编辑器——常规绘图

下面这张图出自:由清华大学出品的 《DeepSeek:从入门到精通》。 作为纯文本生成模型,DeepSeek虽不具备多媒体内容生成接口,但其开放式架构允许通过API接口与图像合成引擎、数据可视化工具等第三方系统进行协同工作,最终…...

DevOps自动化部署详解:从理念到实践

在软件开发日益快速迭代的今天,如何以高效、稳定且可重复的方式将代码变更从开发环境自动部署到生产环境成为企业竞争的重要因素。DevOps 正是在这一背景下应运而生,它打破开发、测试、运维之间的壁垒,通过自动化工具和流程,实现持…...

【拥抱AI】GPT Researcher的诞生

一、GPT Researcher 研究过程总结 GPT Researcher 是一个开源的自主智能体,旨在通过利用人工智能技术实现高效、全面且客观的在线研究。它通过一系列创新的设计和优化,解决了传统研究工具(如 AutoGPT)中存在的问题,如…...

Redis7——基础篇(三)

前言:此篇文章系本人学习过程中记录下来的笔记,里面难免会有不少欠缺的地方,诚心期待大家多多给予指教。 基础篇: Redis(一)Redis(二) 接上期内容:上期完成了Redis的基本…...

ES12 weakRefs的用法和使用场景

ES12 (ECMAScript 2021) 特性总结:WeakRef 1. WeakRef 概述 描述 WeakRef 是 ES12 引入的一个新特性,用于创建对对象的弱引用。弱引用不会阻止垃圾回收器回收对象,即使该对象仍然被弱引用持有。WeakRef 通常与 FinalizationRegistry 结合使…...

【Elasticsearch】搜索时分片路由

Elasticsearch 的Search Shard Routing(搜索分片路由)是一个核心机制,用于在分布式环境中高效地分发和执行搜索请求,确保查询能够快速、准确地返回结果,同时充分利用集群资源并保持系统的高可用性。以下是结合上述内容…...

MySQL登录问题总结

不管何种数据库,使用的第一步都是先登录。 MySQL命令行登录语句:mysql -u username -P port -p -D database_name 登录MySQL的报错一般从报错信息都能得到反馈,常见报错原因分析如下,实例中的以test用户为例,登录环境为…...

一些耳朵起茧子的名词解释

1 web应用 1.1 web应用的概念 Web应用(Web Application) 是一种通过浏览器访问的软件程序,它运行在服务器上,用户通过网络(如互联网或内网)与它进行交互。与传统网站(主要提供静态内容&#x…...

Redis 持久化:从零到掌握

Redis 作为一款广泛使用的内存数据库,虽然核心功能是基于内存提供高性能的数据存取,但在实际应用中,数据的持久化是不可忽视的。毕竟,内存中的数据一旦出现故障或重启,就会面临数据丢失的风险。因此,Redis …...

Mybatis MyBatis框架的缓存 一级缓存

1. 缓存的概念 缓存的概念 在内存中临时存储数据,速度快,可以减少数据库的访问次数。经常需要查询,不经常修改的数据,不是特别重要的数据都适合于存储到缓存中。 2.Mybatis缓存 mybatis包含了一个非常强大的查询缓存特性&#…...

第1章大型互联网公司的基础架构——1.6 RPC服务

你可能在1.1节的引言中注意到业务服务层包括HTTP服务和RPC服务,两者的定位不一样。一般来说,一个业务场景的核心逻辑都是在RPC服务中实现的,强调的是服务于后台系统内部,所谓的“微服务”主要指的就是RPC服务;而HTTP服…...

多个用户如何共用一根网线传输数据

前置知识 一、电信号 网线(如以太网线)中传输的信号主要是 电信号,它携带着数字信息。这些信号用于在计算机和其他网络设备之间传输数据。下面是一些关于网线传输信号的详细信息: 1. 电信号传输 在以太网中,数据是…...

idea-gradle打包运行配置

最近接触了一个项目,使用gradle做为构建工具,这里记录一波,毕竟平时使用的都是maven idea 配置 这里有个坑,Gradle Wrapper,配置的地址gradle下载超时 这个配置修改成阿里的 第一张 第二张 第二张配置的jvm貌似没啥用…...

(新版本onenet)stm32+esp8266/01s mqtt连接onenet上报温湿度和远程控制(含小程序)

物联网实践教程:微信小程序结合OneNET平台MQTT实现STM32单片机远程智能控制 远程上报和接收数据——汇总 前言 之前在学校获得了一个新玩意:ESP-01sWIFI模块,去搜了一下这个小东西很有玩点,远程控制LED啥的,然后我就想…...

一键部署开源DeepSeek并集成到钉钉

一键部署开源DeepSeek并集成到钉钉 简介: DeepSeek发布了两款先进AI模型V3和R1,分别适用于对话AI、内容生成及推理任务。由于官方API流量限制,阿里云推出了私有化部署方案,无需编写代码即可完成部署,并通过计算巢AppF…...

【爬虫基础】第一部分 网络通讯 P1/3

前言 1.知识点碎片化:每个网站实现的技术相似但是有区别,要求我们根据不同的网站使用不同的应对手段。主要是常用的一些网站爬取技术。 2.学习难度:入门比web简单,但后期难度要比web难,在于爬虫工程师与网站开发及运维…...

ES10中Object.fromEntries(),trimStart() ,trimend()的使用方法和使用场景例子

ES10(ECMAScript 2019)新特性总结 ES10(ECMAScript 2019)引入了一些新的方法,增强了 JavaScript 的灵活性和易用性。以下是 Object.fromEntries(),trimStart() 和 trimEnd() 的使用方法、使用场景以及例子…...

车载音频配置(二)

目录 OEM 自定义的车载音频上下文 动态音频区配置 向前兼容性 Android 14 车载音频配置 在 Android 14 中,AAOS 引入了 OEM 插件服务,使你可以更主动地管理由车载音频服务监督的音频行为。 随着新的插件服务的引入,车载音频配置文件中添加了以下更改: • OEM 自定义的车…...

级联选择器多选动态加载

一.级联展示 注:因为级联选择器这里是动态加载,因此如果上来选中一级就需要加载出后面三级的全部数据,依然会很卡,因此,和产品协商把一二级多选框去掉了,这样也避免了你选择一级不能实现子级被全部选中的问…...

华为动态路由-OSPF-骨干区

华为动态路由-OSPF-骨干区 一、OSPF简介 1、OSPF概述 OSPF是一种开放式的、基于链路状态的内部网关协议(IGP),用于在自治系统内部进行路由选择和通信。 OSPF是互联网工程任务组(IETF)定义的标准之一,被广…...

网络安全治理模型

0x02 知识点 安全的目标是提供 可用性 Avialability机密性 confidentiality完整性 Integrity真实性 Authenticity不可否认性 Nonrepudiation 安全治理是一个提供监督、问责和合规性的框架 信息安全系统 Information Security Management System ISMS 策略,工作程…...

企业软件合规性管理:构建高效、安全的软件资产生态

引言 在数字化转型的浪潮下,企业的软件使用方式日益多元化,涉及云端、订阅制、永久授权及浮动许可等多种模式。然而,随着软件资产的增多,企业面临着合规性管理的严峻挑战:非法软件使用、许可证管理不当、软件资产闲置…...

spring微服务+dubbo框架,某一服务启动时提示多个bean存在

在java的springboot项目中使用DubboService的注解的实现类中,在引用本模块的类时,使用的DubboRefrence注解,在启动项目时报错,提示该类需要以一个bean对象,但是存在了两个,把DubboRefrence的注解改成Autowi…...

跟着 Lua 5.1 官方参考文档学习 Lua (3)

文章目录 2.5 – Expressions2.5.1 – Arithmetic Operators2.5.2 – Relational Operators2.5.3 – Logical Operators2.5.4 – Concatenation2.5.5 – The Length Operator2.5.6 – Precedence2.5.7 – Table Constructors2.5.8 – Function Calls2.5.9 – Function Definiti…...

PyTorch 源码学习:阅读经验 代码结构

分享自己在学习 PyTorch 源码时阅读过的资料。本文重点关注阅读 PyTorch 源码的经验和 PyTorch 的代码结构。因为 PyTorch 不同版本的源码实现有所不同,所以笔者在整理资料时尽可能按版本号升序,版本号见标题前[]。最新版本的源码实现还请查看 PyTorch 仓…...