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


Posted in Golang onJune 28, 2021

分布式锁一般有三种实现方式: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
支持指定次数地进行自旋式的锁获取。

以上就是Go 语言下基于 Redis 的分布式锁的详细内容,更多关于Go 分布式锁的资料请关注三水点靠木其它相关文章!

Golang 相关文章推荐
Golang 空map和未初始化map的注意事项说明
Apr 29 Golang
go类型转换及与C的类型转换方式
May 05 Golang
完美解决golang go get私有仓库的问题
May 05 Golang
基于Golang 高并发问题的解决方案
May 08 Golang
再次探讨go实现无限 buffer 的 channel方法
Jun 13 Golang
Go语言应该什么情况使用指针
Jul 25 Golang
Golang表示枚举类型的详细讲解
Sep 04 Golang
golang操作redis的客户端包有多个比如redigo、go-redis
Apr 14 Golang
Golang 遍历二叉树
Apr 19 Golang
实现GO语言对数组切片去重
Apr 20 Golang
Go本地测试解耦任务拆解及沟通详解Go本地测试的思路沟通的重要性总结
Jun 21 Golang
Go语言编译原理之变量捕获
Aug 05 Golang
go语言使用Casbin实现角色的权限控制
Go语言设计模式之结构型模式
浅谈Go语言多态的实现与interface使用
Jun 16 #Golang
再次探讨go实现无限 buffer 的 channel方法
Jun 13 #Golang
Go遍历struct,map,slice的实现
Jun 13 #Golang
go web 预防跨站脚本的实现方式
Jun 11 #Golang
Golang生成Excel文档的方法步骤
You might like
透析PHP的配置文件php.ini
2006/10/09 PHP
PHP URL路由类实例
2013/11/12 PHP
PHP PDOStatement::getColumnMeta讲解
2019/02/01 PHP
jQuery学习笔记(2)--用jquery实现各种模态提示框代码及项目构架
2013/04/08 Javascript
js固定DIV高度,超出部分自动添加滚动条的简单方法
2013/07/10 Javascript
JavaScript 语言基础知识点总结(思维导图)
2013/11/10 Javascript
jQuery 快速结束当前正在执行的动画
2013/11/20 Javascript
jQuery实现的图片分组切换焦点图插件
2015/01/06 Javascript
基于jquery实现的自动补全功能
2015/03/12 Javascript
jQuery实现鼠标悬停背景翻转的黑色导航菜单代码
2015/09/14 Javascript
整理Javascript流程控制语句学习笔记
2015/11/29 Javascript
基于JavaScript如何制作遮罩层对话框
2016/01/26 Javascript
javascript数组定义的几种方法
2017/10/06 Javascript
Vue 图片压缩并上传至服务器功能
2020/01/15 Javascript
从零开始在vue-cli4配置自适应vw布局的实现
2020/06/08 Javascript
JS中的变量作用域(console版)
2020/07/18 Javascript
[49:28]VP vs Optic 2018国际邀请赛小组赛BO2 第二场 8.16
2018/08/17 DOTA
[49:31]DOTA2-DPC中国联赛 正赛 Elephant vs LBZS BO3 第二场 1月29日
2021/03/11 DOTA
python使用PyGame播放Midi和Mp3文件的方法
2015/04/24 Python
详解python中requirements.txt的一切
2017/03/03 Python
Python爬虫使用Selenium+PhantomJS抓取Ajax和动态HTML内容
2018/02/23 Python
python 将print输出的内容保存到txt文件中
2018/07/17 Python
windows下安装Python虚拟环境virtualenvwrapper-win
2019/06/14 Python
Pytorch抽取网络层的Feature Map(Vgg)实例
2019/08/20 Python
Django shell调试models输出的SQL语句方法
2019/08/29 Python
pytorch+lstm实现的pos示例
2020/01/14 Python
python小白学习包管理器pip安装
2020/06/09 Python
OpenCV图片漫画效果的实现示例
2020/08/18 Python
pycharm中如何自定义设置通过“ctrl+滚轮”进行放大和缩小实现方法
2020/09/16 Python
东方通信股份有限公司VC面试题
2014/08/27 面试题
自荐信模版
2013/10/24 职场文书
乡镇党的群众路线教育实践活动领导班子对照检查材料
2014/09/25 职场文书
清明节寄语2015
2015/03/23 职场文书
2015年教师节感恩寄语
2015/03/23 职场文书
实验心得体会范文
2016/01/25 职场文书
MySQL中rank() over、dense_rank() over、row_number() over用法介绍
2022/03/23 MySQL