返回顶部
首页 > 资讯 > 精选 >Go结合Redis怎么实现分布式锁
  • 779
分享到

Go结合Redis怎么实现分布式锁

2023-06-28 06:06:34 779人浏览 独家记忆
摘要

这篇文章主要介绍了Go结合Redis怎么实现分布式锁,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。单Redis实例场景如果熟悉Redis的命令,可能会马上想到使用Redis的

这篇文章主要介绍了Go结合Redis怎么实现分布式,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

    Redis实例场景

    如果熟悉Redis的命令,可能会马上想到使用Redis的set if not exists操作来实现,并且现在标准的实现方式是SET resource_name my_random_value NX PX 30000这串命令,其中:

    • resource_name表示要锁定的资源

    • NX表示如果不存在则设置

    • PX 30000表示过期时间为30000毫秒,也就是30秒

    • my_random_value这个值在所有的客户端必须是唯一的,所有同一key的获取者(竞争者)这个值都不能一样。

    value的值必须是随机数主要是为了更安全的释放锁,释放锁的时候使用脚本告诉Redis:只有key存在并且存储的值和我指定的值一样才能告诉我删除成功。可以通过以下lua脚本实现:

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

    举个例子:客户端A取得资源锁,但是紧接着被一个其他操作阻塞了,当客户端A运行完毕其他操作后要释放锁时,原来的锁早已超时并且被Redis自动释放,并且在这期间资源锁又被客户端B再次获取到。

    使用Lua脚本是因为判断和删除是两个操作,所以有可能A刚判断完锁就过期自动释放了,然后B就获取到了锁,然后A又调用了Del,导致把B的锁给释放了。

    加解锁示例

    package mainimport (   "context"   "errors"   "fmt"   "GitHub.com/brianvoe/gofakeit/v6"   "github.com/go-redis/redis/v8"   "sync"   "time")var client *redis.Clientconst unlockScript = `if redis.call("get",KEYS[1]) == ARGV[1] then    return redis.call("del",KEYS[1])else    return 0end`func lottery(ctx context.Context) error {   // 加锁   myRandomValue := gofakeit.UUID()   resourceName := "resource_name"   ok, err := client.SetNX(ctx, resourceName, myRandomValue, time.Second*30).Result()   if err != nil {      return err   }   if !ok {      return errors.New("系统繁忙,请重试")   }   // 解锁   defer func() {      script := redis.NewScript(unlockScript)      script.Run(ctx, client, []string{resourceName}, myRandomValue)   }()   // 业务处理   time.Sleep(time.Second)   return nil}func main() {   client = redis.NewClient(&redis.Options{      Addr: "127.0.0.1:6379",   })   var wg sync.WaitGroup   wg.Add(2)   go func() {      defer wg.Done()      ctx, _ := context.WithTimeout(context.Background(), time.Second*3)      err := lottery(ctx)      if err != nil {         fmt.Println(err)      }   }()   go func() {      defer wg.Done()      ctx, _ := context.WithTimeout(context.Background(), time.Second*3)      err := lottery(ctx)      if err != nil {         fmt.Println(err)      }   }()   wg.Wait()}

    我们先看lottery()函数,这里模拟一个抽奖操作,在进入函数时,先使用SET resource_name my_random_value NX PX 30000加锁,这里使用UUID作为随机值,如果操作失败,直接返回,让用户重试,如果成功在defer里面执行解锁逻辑,解锁逻辑就是执行前面说到得lua脚本,然后再进行业务处理。

    我们在main()函数里面执行了两个goroutine并发调用lottery()函数,其中有一个操作会因为拿不到锁而直接失败。

    小结

    • 生成随机值

    • 使用SET resource_name my_random_value NX PX 30000加锁

    • 如果加锁失败,直接返回

    • defer添加解锁逻辑,保证在函数退出的时候会执行

    • 执行业务逻辑

    多Redis实例场景

    在单实例情况下,如果这个实例挂了,那么所有请求都会因为拿不到锁而失败,所以我们需要多个分布在不同机器上的Redis实例,并且拿到其中大多数节点的锁才能加锁成功,这也就是RedLock算法。它其实也是基于上面的单实例算法的,只是我们需要同时对多个Redis实例获取锁。

    加解锁示例

    package mainimport (   "context"   "errors"   "fmt"   "github.com/brianvoe/gofakeit/v6"   "github.com/go-redis/redis/v8"   "sync"   "time")var clients []*redis.Clientconst unlockScript = `if redis.call("get",KEYS[1]) == ARGV[1] then    return redis.call("del",KEYS[1])else    return 0end`func lottery(ctx context.Context) error {   // 加锁   myRandomValue := gofakeit.UUID()   resourceName := "resource_name"   var wg sync.WaitGroup   wg.Add(len(clients))   // 这里主要是确保不要加锁太久,这样会导致业务处理的时间变少   lockCtx, _ := context.WithTimeout(ctx, time.Millisecond*5)   // 成功获得锁的Redis实例的客户端   successClients := make(chan *redis.Client, len(clients))   for _, client := range clients {      go func(client *redis.Client) {         defer wg.Done()         ok, err := client.SetNX(lockCtx, resourceName, myRandomValue, time.Second*30).Result()         if err != nil {            return         }         if !ok {            return         }         successClients <- client      }(client)   }   wg.Wait() // 等待所有获取锁操作完成   close(successClients)   // 解锁,不管加锁是否成功,最后都要把已经获得的锁给释放掉   defer func() {      script := redis.NewScript(unlockScript)      for client := range successClients {         go func(client *redis.Client) {            script.Run(ctx, client, []string{resourceName}, myRandomValue)         }(client)      }   }()   // 如果成功加锁得客户端少于客户端数量的一半+1,表示加锁失败   if len(successClients) < len(clients)/2+1 {      return errors.New("系统繁忙,请重试")   }   // 业务处理   time.Sleep(time.Second)   return nil}func main() {   clients = append(clients, redis.NewClient(&redis.Options{      Addr: "127.0.0.1:6379",      DB:   0,   }), redis.NewClient(&redis.Options{      Addr: "127.0.0.1:6379",      DB:   1,   }), redis.NewClient(&redis.Options{      Addr: "127.0.0.1:6379",      DB:   2,   }), redis.NewClient(&redis.Options{      Addr: "127.0.0.1:6379",      DB:   3,   }), redis.NewClient(&redis.Options{      Addr: "127.0.0.1:6379",      DB:   4,   }))   var wg sync.WaitGroup   wg.Add(2)   go func() {      defer wg.Done()      ctx, _ := context.WithTimeout(context.Background(), time.Second*3)      err := lottery(ctx)      if err != nil {         fmt.Println(err)      }   }()   go func() {      defer wg.Done()      ctx, _ := context.WithTimeout(context.Background(), time.Second*3)      err := lottery(ctx)      if err != nil {         fmt.Println(err)      }   }()   wg.Wait()   time.Sleep(time.Second) }

    在上面的代码中,我们使用Redis的多数据库模拟多个Redis master实例,一般我们会选择5个Redis实例,真实环境中这些实例应该是分布在不同机器上的,避免同时失效。
    在加锁逻辑里,我们主要是对每个Redis实例执行SET resource_name my_random_value NX PX 30000获取锁,然后把成功获取锁的客户端放到一个channel里(这里使用slice可能有并发问题),同时使用sync.WaitGroup等待所以获取锁操作结束。
    然后添加defer释放锁逻辑,释放锁逻辑很简单,只是把成功拿到的锁给释放掉即可。
    最后判断成功获取到的锁的数量是否大于一半,如果没有得到一半以上的锁,说明加锁失败。
    如果加锁成功接下来就是进行业务处理。

    小结

    • 生成随机值

    • 并发给每个Redis实例使用SET resource_name my_random_value NX PX 30000加锁

    • 等待所有获取锁操作完成

    • defer添加解锁逻辑,保证在函数退出的时候会执行,这里先defer再判断是因为有可能获取到一部分Redis实例的锁,但是因为没有超过一半,还是会判断为加锁失败

    • 判断是否拿到一半以上Redis实例的锁,如果没有说明加锁失败,直接返回

    • 执行业务逻辑

    感谢你能够认真阅读完这篇文章,希望小编分享的“Go结合Redis怎么实现分布式锁”这篇文章对大家有帮助,同时也希望大家多多支持编程网,关注编程网精选频道,更多相关知识等着你来学习!

    --结束END--

    本文标题: Go结合Redis怎么实现分布式锁

    本文链接: https://lsjlt.com/news/317059.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    猜你喜欢
    • Go结合Redis怎么实现分布式锁
      这篇文章主要介绍了Go结合Redis怎么实现分布式锁,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。单Redis实例场景如果熟悉Redis的命令,可能会马上想到使用Redis的...
      99+
      2023-06-28
    • Go结合Redis用最简单的方式实现分布式锁
      目录前言单Redis实例场景加解锁示例小结多Redis实例场景加解锁示例小结总结前言 在项目中我们经常有需要使用分布式锁的场景,而Redis是实现分布式锁最常见的一种方式,并且我们也...
      99+
      2024-04-02
    • 怎么用Go+Redis实现分布式锁
      这篇文章主要介绍怎么用Go+Redis实现分布式锁,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!为什么需要分布式锁用户下单锁住 uid,防止重复下单。库存扣减锁住库存,防止超卖。余额扣减锁住账户,防止并发操作。分布式...
      99+
      2023-06-22
    • Redis分布式锁怎么实现
      这篇文章给大家分享的是有关Redis分布式锁怎么实现的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。分布式锁一般有三种实现方式:1、数据库乐观锁;2、基于Redis的分布式锁;3、...
      99+
      2024-04-02
    • Redis怎么实现分布式锁
      这篇文章主要介绍“Redis怎么实现分布式锁”,在日常操作中,相信很多人在Redis怎么实现分布式锁问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Redis怎么实现分布式锁”的疑惑有所帮助!接下来,请跟着小编...
      99+
      2023-06-02
    • 分布式锁的原理及Redis怎么实现分布式锁
      这篇文章主要介绍“分布式锁的原理及Redis怎么实现分布式锁”,在日常操作中,相信很多人在分布式锁的原理及Redis怎么实现分布式锁问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解...
      99+
      2023-02-02
      redis
    • Redis中怎么实现分布式锁
      本篇内容介绍了“Redis中怎么实现分布式锁”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!为什么需要分布式...
      99+
      2024-04-02
    • 怎么用Redis实现分布式锁
      本文小编为大家详细介绍“怎么用Redis实现分布式锁”,内容详细,步骤清晰,细节处理妥当,希望这篇“怎么用Redis实现分布式锁”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。单机...
      99+
      2024-04-02
    • Redis怎么样实现分布式锁
      这篇文章主要介绍了Redis怎么样实现分布式锁,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。在一个分布式系统中,会遇到一些需要对多个节点共享的资源加锁的情况,这个时候需要用到...
      99+
      2023-06-21
    • spring redis分布式锁怎么实现
      在Spring中实现Redis分布式锁可以使用RedisTemplate来操作Redis进行加锁和解锁。 首先,我们需要定义一个分布...
      99+
      2023-10-27
      spring redis
    • Redis——》实现分布式锁
      推荐链接:     总结——》【Java】     总结——》【Mysql】     总结——》【Redis】     总结——》【Kafka】     总结——》【Spring】     总结—...
      99+
      2023-09-03
      redis 分布式 过期 lua
    • Redis实现分布式锁
      单体锁存在的问题 在单体应用中,如果我们对共享数据不进行加锁操作,多线程操作共享数据时会出现数据一致性问题。 (下述实例是一个简单的下单问题:从redis中获取库存,检查库存是否够,>0才允许下单) 我们的解决办法通常是加锁。如下加单体锁...
      99+
      2023-08-16
      分布式 java jvm
    • Go与Redis实现分布式互斥锁和红锁
      目录前言互斥锁TryLock和Unlock实现Lock实现实现看门狗机制看门狗实现红锁加锁实现看门狗实现解锁实现前言 在项目中我们经常有需要使用分布式锁的场景,而Redis是实现分布式锁最常见的一种方式,这篇文章主要是使...
      99+
      2024-04-02
    • 怎么通过redis实现分布式锁
      本篇文章展示了通过redis实现分布式锁的具体操作,代码简明扼要容易理解,绝对能让你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。分布式锁是控制分布式系统之间同步访问共享资源的一种方式。在分布式系统中,...
      99+
      2024-04-02
    • Redisson中怎么实现Redis分布式锁
      Redisson中怎么实现Redis分布式锁,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Redis几种架构Redis发展到现在,几种常见的部署架构有:单机模式;主从模式;...
      99+
      2023-06-20
    • redis分布式锁的实现
      一、使用分布式锁要满足的几个条件:1、系统是一个分布式系统(关键是分布式,单机的可以使用ReentrantLock或者synchronized代码块来实现)2、共享资源(各个系统访问同一个资源,资源的载体可...
      99+
      2024-04-02
    • Redis Template实现分布式锁
      今天就跟大家聊聊有关Redis Template实现分布式锁,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。可靠性首先,为了确保分布式锁可用,我们至少...
      99+
      2024-04-02
    • PHP+Redis实现分布式锁
      目录 一、分布式锁概述 二、redis实现锁的命令 1、redis实现锁的命令 3、释放锁的步骤 三、PHP+redis分布式锁示例 四、redis集群分布式锁 一、分布式锁概述         在分布式环境下,各个线程通过对公共资...
      99+
      2023-09-15
      分布式锁
    • Redis实现分布式锁(SETNX)
      目录 1、什么是分布式锁 2、分布式锁应具备的条件         3、为什么使用分布式锁 4、SETNX介绍 5、分布式锁实现 6、效果演示 7、Redisson分布式锁详解 8、Lua脚本实现可重入分布式锁 1、什么是分布式锁  ...
      99+
      2023-10-21
      redis 分布式 java spring boot 后端
    • python实现redis分布式锁
      #!/usr/bin/env python # coding=utf-8 import time import redis class RedisLock(object): def __init__(self, key): ...
      99+
      2023-01-31
      分布式 python redis
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作