Go 语言下基于Redis分布式锁的实现方式

作者:Spongecaptain 时间:2024-04-25 13:17:24 

分布式锁一般有三种实现方式:1. 数据库乐观锁;2. 基于Redis的分布式锁;3. 基于ZooKeeper的分布式锁。本篇博客将介绍第二种方式,基于Redis实现分布式锁。虽然网上已经有各种介绍Redis分布式锁实现的博客,然而他们的实现却有着各种各样的问题,为了避免误人子弟,本篇博客将详细介绍如何正确地实现Redis分布式锁。

项目地址: https://github.com/Spongecaptain/redisLock

1. Go 原生的互斥锁

Go 原生的互斥锁即 sync 包下的 Mutex 结构体,利用此结构体的 Lock 以及 Unlock 方法能够实现锁的占据以及释放。

关于 sync.Mutex,我们可以总结出如下的特性:

  • 支持自旋锁,在并发冲突不严重的背景下提高锁的使用效率;

  • 支持锁的公平性,能够避免锁导致的线程饥饿问题;

  • 不支持锁的重入,持有锁的协程再次申请锁资源会导致死锁;

  • 任何协程都可以调用 Mutex.Unlock 方法来解锁,并不只允许占据锁资源的协程进行解锁;

  • 不支持超时锁获取,也不支持 TryLock 机制

Go 语言中 sync.Mutex 的特性与 Java 中 java.util.concurrent.locks.Lock 相比,API 语义简单不少,这也符合 Go 语言对于简单化的追求。

下面看看本项目-基于 Redis 的分布式锁能够提供哪些分布锁特性。

2. redisLock 的特性

github-redisLock 是一个基于 go-redis/redis 客户端的 Redis 分布式锁。其拥有的如下的特性:

  • 原子性:利用 Lua 脚本实现原子性语义;

  • 阻塞唤醒:利用 Redis 的发布订阅来实现锁的阻塞唤醒;

  • 锁自动过期:避免因为宕机导致的死锁问题;

  • 锁的自动续期:利用 Go 协程实现锁资源的自动续期,避免出现业务时间>锁超时时间导致并发安全问题

  • TryLock:尝试获取一次锁,获取失败后阻塞

  • 自旋锁:提供自旋锁 API 来实现分布式锁的自旋获取

github-redisLock 同时不支持如下特性:

重入性:分布式锁不可重入,Go 语言并没有优雅的方式来实现 Java 中的 ThreadLocal 机制
非公平性:分布式锁存在非公平问题,在极端情况下会导致饥饿问题

3. Quick Start

Install redisLock:


go get github.com/Spongecaptain/redisLock


 

Create redis client:


import(
"github.com/go-redis/redis"
)
var redisClient = redis.NewClient(&redis.Options{
Addr:     "localhost:6379",
Password: "", // no password set
DB:       0,  // use default DB
})

Create redisLock:


key := "reids-lock-key"
value := "redis-lock-value"
lock := redisLock.NewRedisLock(redisClient, key, value)

err := lock.Lock()
if err != nil {
 fmt.Println(err.Error())
 return
}
fmt.Println("get redis lock success")
defer func() {
 err = lock.Unlock()
 if err != nil {
   fmt.Println(err.Error())
   return
 }
 fmt.Println("release redis lock success")
}()

4. API 说明

(1)构造分布式锁实例
利用 NewRedisLock 以及 NewRedisLockWithExpireTime 函数能够构造出一个分布式锁实例,NewRedisLockWithExpireTime 的区别在于其能够自定义锁的过期时间。

NewRedisLock 方法接收的 key 决定了分布式锁的粒度,value 决定了只有 value 值相同才能够进行解锁。

(2)TryLock
TryLock 仅尝试一次锁的获取,如果失败,那么不会阻塞,直接返回。

(3)Lock
Lock 会不断尝试索取分布式锁,这会导致调用此方法的协程阻塞。

(4)Unlock
Unlock 方法用于解锁,由于涉及网络通信,解锁可能失败, error!=nil 意味着解锁失败。

(5)LockWithTimeout
Lock 方法会在获取锁资源成功或者超时后返回。

(6)SpinLock
支持指定次数地进行自旋式的锁获取。

来源:https://spongecaptain.cool/post/go/redislock/

标签:Go,Redis,分布式锁
0
投稿

猜你喜欢

  • Python解释器以及PyCharm的安装教程图文详解

    2021-04-09 11:56:32
  • 页面重构应注意的repaint和reflow

    2011-03-31 17:11:00
  • 关于pycharm 切换 python3.9 报错 ‘HTMLParser‘ object has no attribute ‘unescape‘ 的问题

    2023-11-25 16:27:55
  • pandas使用apply多列生成一列数据的实例

    2022-11-16 20:23:59
  • python利用hook技术破解https的实例代码

    2021-10-24 17:20:15
  • Asp定时执行操作、各种网页定时操作详解

    2008-06-10 17:32:00
  • python 在某.py文件中调用其他.py内的函数的方法

    2021-02-25 23:56:09
  • 解决TensorFlow模型恢复报错的问题

    2022-06-15 16:24:31
  • centos 6.9安装mysql的详细教程

    2024-01-13 06:51:15
  • MySQL中Order By多字段排序规则代码示例

    2024-01-22 01:10:35
  • python数据化运营的重要意义

    2021-05-05 21:02:27
  • thinkphp5加layui实现图片上传功能(带图片预览)

    2023-06-13 01:09:45
  • python实现Dijkstra静态寻路算法

    2021-06-10 07:41:09
  • python 包实现JSON 轻量数据操作

    2022-11-13 05:40:32
  • Python如何把十进制数转换成ip地址

    2023-02-20 21:36:55
  • 详解python pandas 分组统计的方法

    2021-01-30 05:33:48
  • python中的路径拼接问题

    2021-02-06 11:28:56
  • 详解JavaScript中的Object.is()与"==="运算符总结

    2024-04-22 12:50:25
  • Pycharm搭建一个Django项目的方法步骤

    2022-04-12 12:46:41
  • Golang中HttpRouter路由的使用详解

    2024-04-30 10:04:23
  • asp之家 网络编程 m.aspxhome.com