Go微服务: redis分布式锁

概述

  • 在分布式系统中,并发控制和数据一致性是至关重要的问题
  • 当多个服务或进程需要访问和修改共享资源时,我们必须确保在同一时间只有一个服务或进程能够执行操作,以防止数据竞争和不一致,这就是分布式锁要解决的问题
  • Redis 作为一个高性能的键值存储系统,经常被用作实现分布式锁的工具。
  • Redis 的 SETNX、EXPIRE、DEL 等命令可以组合起来实现一个简单的分布式锁,但是,直接使用这些命令可能会引入一些复杂的逻辑和潜在的错误
  • 因此,许多开发者选择使用现成的库来简化分布式锁的实现
  • 在 Go 语言中,github.com/go-redsync/redsync 是一个流行的库,它基于 Redis 提供了分布式锁的抽象和实现

Redis 分布式锁的基本原理


Redis 分布式锁通常基于以下原理

  • 加锁:使用 SETNX 命令尝试设置一个键值对,如果键不存在则设置成功(返回 1),否则设置失败(返回 0)。设置成功的进程获得了锁
  • 设置过期时间:为了防止死锁,通常会为锁设置一个过期时间,使用 EXPIRE 命令
  • 解锁:当进程完成操作后,需要删除之前设置的键值对来释放锁,使用 DEL 命令
  • 处理锁过期:如果持有锁的进程在锁过期之前未能完成操作并释放锁,其他进程可以重新获取锁

示例程序

package mainimport ("fmt""time""github.com/go-redsync/redsync/v4""github.com/go-redsync/redsync/v4/redis/goredis/v9"goredislib "github.com/redis/go-redis/v9""sync"
)func RedisLock(wg *sync.WaitGroup) {// 初始化锁客户端client := goredislib.NewClient(&goredislib.Options{Addr:     "127.0.0.1:6380",Password: "123456_redis",Username: "root",DB:       0,})pool := goredis.NewPool(client) // or, pool := redigo.NewPool(...)rs := redsync.New(pool)// 初始化锁mutexname := "my-global-mutex"mutex := rs.NewMutex(mutexname, redsync.WithExpiry(30*time.Second))// 开始锁定fmt.Println("Lock()....")if err := mutex.Lock(); err != nil {panic(err)}// 自己的一些业务逻辑fmt.Println("Get Lock!!!")time.Sleep(time.Second * 1)// 开始解锁fmt.Println("UnLock()")if ok, err := mutex.Unlock(); !ok || err != nil {panic("unlock failed")}fmt.Println("Released Lock!!!")
}func main() {// 测试程序var wg sync.WaitGroupfor i := 0; i < 3; i++ {wg.Add(1)go RedisLock(&wg)}wg.Wait()
}
  • 程序运行,输出

    Lock()....
    Lock()....
    Lock()....
    Get Lock!!!
    UnLock()
    Released Lock!!!
    Get Lock!!!
    UnLock()
    Released Lock!!!
    Get Lock!!!
    UnLock()
    Released Lock!!!
    
  • 前三个Lock() 代表程序在排队阶段

  • 后面接着三次输出,每次都有: Get Lock!!!, UnLock(), Released Lock!!!

  • 每个过程都是 获取锁,解锁,释放锁

  • 所以在高并发的时候,使用redis的分布式锁可以解决资源数据的竞争

  • 特别注意的是: mutexname := "my-global-mutex" 这个锁的名称在 redis 中使用的时候会生成,当锁被全部释放后,就会自动释放,而且这个锁的名称最好也自行规范下

    // 以下是 docker 内的redis容器cli
    127.0.0.1:6379> keys *
    1) "my-global-mutex"
    // 稍后再次查询后,锁的key和value消失
    127.0.0.1:6379> keys *
    (empty array)
    
  • 另外,如果锁在规定的时间内没有完成工作,那么在设定的过期时间后就会自动结束,如果不设置过期时间,走系统内的过期时间

使用 redsync 实现 Redis 分布式锁

  • redsync 库提供了一个高级接口,使得在 Go 语言中使用 Redis 分布式锁变得更加简单
  • 下面是如何使用 redsync 实现分布式锁的步骤:
    • 初始化 Redis 客户端:首先,你需要初始化一个 Redis 客户端。在上述代码中,使用了 github.com/redis/go-redis/v9 库来创建 Redis 客户端,并为其设置了地址、密码、用户名和数据库编号
    • 创建锁池:redsync 需要一个锁池(Pool)来管理锁,使用 goredis.NewPool 方法来创建一个基于 go-redis 客户端的锁池
    • 创建分布式锁:使用 redsync.New 方法来创建一个 Redsync 实例,并使用 NewMutex 方法来创建一个分布式锁,可以为锁指定一个名称,并设置锁的过期时间。
    • 加锁:调用 Lock 方法来尝试获取锁,如果加锁失败(例如,其他进程已经持有锁),则该方法会返回一个错误
    • 执行业务逻辑:在成功获取锁之后,执行您的业务逻辑
    • 解锁:完成业务逻辑后,调用 Unlock 方法来释放锁,请注意,Unlock 方法会返回一个布尔值和一个错误,布尔值表示是否成功释放了锁,而错误则表示在解锁过程中是否发生了错误

注意事项

  • 死锁:务必为锁设置合理的过期时间,以防止死锁
  • 重试机制:当加锁失败时,您可能需要实现一个重试机制来等待一段时间后再次尝试加锁
  • 解锁失败:虽然 Unlock 方法会尝试释放锁,但在某些情况下(例如,Redis 实例崩溃或网络问题),解锁可能会失败。因此,您应该始终检查 Unlock 方法的返回值,并在必要时处理解锁失败的情况
  • 并发控制:在分布式系统中,除了使用分布式锁之外,您还需要考虑其他并发控制策略,例如消息队列、事件驱动架构等
  • 安全性:确保 Redis 实例的安全性,包括使用强密码、限制访问权限等。此外,如果您的系统对安全性有更高的要求,您可能需要考虑使用更复杂的分布式锁实现,例如基于 ZooKeeper 或 etcd 的分布式锁

参考文档

  • https://redis.io/docs/latest/develop/use/patterns/distributed-locks/
  • https://redis.uptrace.dev/zh/guide/go-redis-option.html#redis-client

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/856990.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++LeetCode】【热题100】字母异位词分组【中等】-不同效率的题解【3】

题目&#xff1a; 暴力方法&#xff1a; class Solution { public:vector<vector<string>> groupAnagrams(vector<string>& strs) {std::unordered_set<std::string> uniqueWord;//单词字符唯一化集合vector<vector<std::string>>…

MySQL:MySQL分组排序函数rank()、row_number()、dense_rank()与partition by结合使用

一、前言 在 MySQL 中&#xff0c;虽然标准的 SQL 函数 RANK(), ROW_NUMBER(), 和 DENSE_RANK() 是 SQL 标准的一部分&#xff0c;但早期的 MySQL 版本并不直接支持这些窗口函数。然而&#xff0c;从 MySQL 8.0 开始&#xff0c;这些函数被引入以支持窗口函数&#xff08;也称为…

深入Android S (12.0) 探索Framework之输入子系统InputDispatcher的流程

Framework层之输入系统 第一篇 深入Android S (12.0) 探索Framework之输入系统IMS的构成与启动 第二篇 深入Android S (12.0) 探索Framework之输入子系统InputReader的流程 第三篇 深入Android S (12.0) 探索Framework之输入子系统InputDispatcher的流程 文章目录 Framework层…

SMTP、POP3、IMAP4学习

文章目录 前言SMTPPOP3IMAP4区别与联系 前言 常见的邮件协议包括SMTP、POP3和IMAP4。这些协议都隶属于TCP/IP协议簇&#xff0c;属于应用层&#xff0c;分别通过TCP端口25、110和143建立连接。电子邮件的发送和接收过程依赖于这些协议。 SMTP &#xff08;Simple Mail Trans…

数据标注-语音标注

声音是由物体振动产生的&#xff0c;通过介质传播到人耳中。 音高是指人听到的声音的高低&#xff0c;它与物体振动的频率成正比。物体振动的频率越快&#xff0c;我们听到的声音就越高&#xff1b;物体振动的频率越慢&#xff0c;我们听到的声音就越低。 音素是语言中声音的…

微信小程序-界面提示框和消息

一.Loading加载框 小程序提供了wx.showLoading用来在加载界面的时候使用&#xff0c;比如加载图片和数据的时候可以使用。 常常和wx.hideLoading()配合使用&#xff0c;否则加载框一直存在。 其效果如下&#xff1a; 代码如下&#xff1a; //显示加载消息wx.showLoading({//提…

【机器学习】Lasso回归:稀疏建模与特征选择的艺术

&#x1f308;个人主页: 鑫宝Code &#x1f525;热门专栏: 闲话杂谈&#xff5c; 炫酷HTML | JavaScript基础 ​&#x1f4ab;个人格言: "如无必要&#xff0c;勿增实体" 文章目录 Lasso回归&#xff1a;稀疏建模与特征选择的艺术引言一、Lasso回归简介1.1 基本…

Leetcode Java学习记录——代码随想录数组篇

因为是初学Java&#xff0c;刷题语言从Python切换过来有很多小习惯会出问题。 除此之外也算二刷代码随想录&#xff0c;仍有很多细节问题&#xff0c;特此记录。 文章目录 Java 易错点声明位运算lengthfor定义数组数组打印无穷大 二分法不变量 Java 易错点 声明 Java中变量需…

丰臣秀吉-读书笔记五

如今直面自己一生中的最高点&#xff0c;加之平日里的觉悟与希冀&#xff0c;此时此地他“一定要死得其所”。 “武士之道&#xff0c;便是在死的瞬间决定一生或华或实。一生谨慎、千锤百炼&#xff0c;如果在死亡这条路上一步走错&#xff0c;那么一生的言行便全部失去真意&am…

帕金森的锻炼方式

帕金森病&#xff0c;这个看似陌生的名词&#xff0c;其实离我们并不遥远。它是一种常见的神经系统疾病&#xff0c;影响着许多中老年人的生活质量。虽然帕金森病目前尚无根治之法&#xff0c;但通过科学合理的日常锻炼&#xff0c;可以有效缓解病情&#xff0c;提高生活质量。…

通用大模型与垂直大模型之争!

随着人工智能技术的飞速发展&#xff0c;大模型已经成为该领域的一个重要趋势。然而&#xff0c;通用大模型和垂直大模型在落地场景和应用上存在明显的差异&#xff0c;究竟谁能在这一领域中占据优势&#xff0c;成为了当前讨论的焦点。  通用大模型具有更广泛的应用场景&…

录的视频太大怎么压缩?这几款软件真的很不错!

在数字化时代&#xff0c;视频已成为我们日常生活和工作中不可或缺的一部分。无论是记录生活点滴&#xff0c;还是制作工作汇报&#xff0c;视频都以其直观、生动的特点赢得了我们的青睐。然而&#xff0c;随着视频质量的提升&#xff0c;视频文件的大小也在不断增加&#xff0…

内容安全复习 2 - 网络信息内容的获取与表示

文章目录 信息内容的获取网络信息内容的类型网络媒体信息获取方法 信息内容的表示视觉信息视觉特征表达文本特征表达音频特征表达 信息内容的获取 网络信息内容的类型 网络媒体信息 传统意义上的互联网网站公开发布信息&#xff0c;网络用户通常可以基于网络浏览器获得。网络…

API低代码平台介绍5-数据库记录修改功能

数据库记录修改功能 在上篇文章中我们介绍了如何插入数据库记录&#xff0c;本篇文章会沿用上篇文章的测试数据&#xff0c;介绍如何使用ADI平台定义一个修改目标数据库记录的接口&#xff0c;包括 单主键单表修改、复合主键单表修改、多表修改&#xff08;整合前两者&#xff…

每日练题(py,c,cpp).6_19,6_20

检验素数 from math import sqrt a int(input("请输入一个数&#xff1a;")) for i in range(2,int(sqrt(a))):if a%i 0:print("该数不是素数")breakelse: print("该数是素数")# # 1既不是素数也不是合数 # #可以用flag做标志位 # b int(…

VUE element-ui之table表格横向展示(表尾汇总)_elementui横向表格

</el-table><section><el-table:show-header“false” :data“tableDataBll” border style“width: 100%;” <el-table-column prop"total" align"center" /><el-table-column prop"totalOrder" /><el-table-col…

视频智能分析平台智能边缘分析一体机安防监控平台打手机检测算法工作原理介绍

智能边缘分析一体机的打手机检测算法是一种集成了计算机视觉和人工智能技术的先进算法&#xff0c;专门用于实时监测和识别监控画面中的打手机行为。以下是关于该算法的详细介绍&#xff1a; 工作原理 1、视频流获取&#xff1a; 智能边缘分析一体机首先通过连接的视频监控设…

国内composer镜像有哪些

阿里云 Composer 全量镜像 镜像地址: https://mirrors.aliyun.com/composer/官方地址: https://mirrors.aliyun.com/composer/index.html镜像类型: 全量镜像更新时间: 1分钟备注: 阿里云CDN加速&#xff0c;更新速度快&#xff0c;推荐使用。腾讯云 Composer 全量镜像 镜像地址…

【UIDynamic-动力学-附着行为-刚性附着 Objective-C语言】

一、接下来,我们来说这个附着行为啊, 1.我们之前举过例子,一个车坏了,另外一个车,拉着这个车,就是附着行为啊, 这个里边呢,我们新建一个项目, Name:09-附着行为-刚性附着, 附着行为呢,分为两个大类: 1)刚性附着 2)弹性附着 刚性附着,指的就是,两个物体之间…

lftp工具速成

登录 lftp user:passwd192.168.0.123:21显示 ls设置本地目录 lcd /home/xxx/ftp下载 // 下载文件 get 1.log// 下载多个文件 mget *.log// 下载目录 mirror dir_name上传 // 下载文件 put 1.log// 下载多个文件 mput *.log// 下载目录 mirror -R dir_name退出 exit