redis实现分布式锁,go实现完整code
Redis分布式锁
Redis 分布式锁是一种使用 Redis 数据库实现分布式锁的方式,可以保证在分布式环境中同一时间只有一个实例可以访问共享资源。
实现机制
以下是实现其加锁步骤:
获取锁
在 Redis 中,一个相同的key
代表一把锁。是否拥有这把锁,需要判断key
和value
是否是自己设置的,同时还要判断锁是否已经过期。
- 首先通过get命令去获取锁,如果获取不到说明还没有加锁
- 如果还没有加锁我们就可以去通过set命令去加锁,并且需要设置一个expire过期时间防止成为一个长生不老锁,那如果业务还没有执行完锁就释放了怎么办呢?这个后面会提到续锁
- 如果获取到了key说明已经被其他实例抢到了锁,加锁失败
- 加锁失败还需要根据一些操作例如超时时间内去重试加锁,直到加锁成功或者超时
这些操作都需要原子性操作,需要用lua脚本进行封装
lock.lua
val = redis.call('get', KEYS[1])
if val == false thenreturn redis.call('set', KEYS[1], ARGV[1], 'EX', ARGV[2])
elseif val == ARGV[1] thenredis.call('expire', KEYS[1], ARGV[2])return 'OK'
elsereturn ''
end
释放锁
释放锁的时候就是把key删除,不过删除的时候需要判断是不是自己加的锁
unlock.lua
if redis.call('get', KEYS[1]) == ARGV[1] thenreturn redis.call('del', KEYS[1])
elsereturn 0
end
Go 实现分布式锁
结构体字段配置
// redis客户端连接
type Client struct {client redis.CmdablevarFunc func() stringg singleflight.Group
}// 锁的结构体
type Lock struct {client redis.Cmdablekey stringvalue stringexpiration time.Durationunlock chan struct{}unlockOne sync.Once
}// NewClient creates a *Client
func NewClient(client redis.Cmdable) *Client {return &Client{client: client,varFunc: func() string {return uuid.New().String()},}
}// 重试策略
type RetryStrategy interface {// Next determines the time interval for Lock// and whether Lock to retryNext() (time.Duration, bool)
}// 周期性重试
type FixedIntervalRetry struct {Interval time.DurationMax intcnt int
}
lua 脚本,使用go的embed映射到luaLock string
var (ErrFailedToPreemptLock = errors.New("redis-lock: failed to lock")ErrLockNotHold = errors.New("redis-lock: lock not hold")ErrLockTimeout = errors.New("redis-lock: lock timeout")//go:embed lua/unlock.lualuaUnlock string//go:embed lua/refresh.lualuaRefresh string//go:embed lua/lock.lualuaLock string
)
加锁Lock
加锁时有两种方案,一种是比较简单的( TryLock )尝试加锁,只需要传个过期时间,另一种是比较完善的( Lock )加锁,会有超时策略等
func newLock(client redis.Cmdable, key string, value string, expiration time.Duration) *Lock {return &Lock{client: client,key: key,value: value,expiration: expiration,unlock: make(chan struct{}, 1),}
}// TryLock tries to acquire a lock
func (c *Client) TryLock(ctx context.Context,key string,expiration time.Duration) (*Lock, error) {val := c.varFunc()ok, err := c.client.SetNX(ctx, key, val, expiration).Result()if err != nil {return nil, err}if !ok {return nil, ErrFailedToPreemptLock}return newLock(c.client, key, val, expiration), nil
}// Lock tries to acquire a lock with timeout and retry strategy
func (c *Client) Lock(ctx context.Context,key string,expiration time.Duration,timeout time.Duration, retry RetryStrategy) (*Lock, error) {var timer *time.Timerval := c.varFunc()for {lCtx, cancel := context.WithTimeout(ctx, timeout)res, err := c.client.Eval(lCtx, luaLock, []string{key}, val, expiration.Seconds()).Result()cancel()if err != nil && !errors.Is(err, context.DeadlineExceeded) {return nil, err}if res == "OK" {return newLock(c.client, key, val, expiration), nil}interval, ok := retry.Next()if !ok {return nil, ErrLockTimeout}if timer == nil {timer = time.NewTimer(interval)} else {timer.Reset(interval)}select {case <-timer.C:case <-ctx.Done():return nil, ctx.Err()}}
}
解锁unLock
// Unlock releases the lock
func (l *Lock) Unlock(ctx context.Context) error {res, err := l.client.Eval(ctx, luaUnlock, []string{l.key}, l.value).Int64()defer func() {l.unlockOne.Do(func() {l.unlock <- struct{}{}close(l.unlock)})}()if errors.Is(err, redis.Nil) {return ErrLockNotHold}if err != nil {return err}if res != 1 {return ErrLockNotHold}return nil
}
小结
- 使用分布式锁本身会有各种各样的问题,需要自己去处理异常情况例如超时等
- 对锁的操作一定要判断是不是自己加的那把锁,否则会误删会导致业务错误
- 对锁的续约部分我们下一篇再讲
本文go的代码是完整的,可以直接copy使用,有兴趣的小伙伴可以去使用一下
相关文章:

redis实现分布式锁,go实现完整code
Redis分布式锁 Redis 分布式锁是一种使用 Redis 数据库实现分布式锁的方式,可以保证在分布式环境中同一时间只有一个实例可以访问共享资源。 实现机制 以下是实现其加锁步骤: 获取锁 在 Redis 中,一个相同的key代表一把锁。是否拥有这把锁&…...

解析日期、编码
解析日期 这里指的是将字符串或者object类型的日期,转换成panda或python的日期类型。 主要的是dtype的变化:object / str —> datetime64[ns] # modules well use import pandas as pd import numpy as np import seaborn as sns import datetime# …...
【Qt】QApplication::restoreOverrideCursor():恢复鼠标光标到原始状态的用法解析
restoreOverrideCursor() 是 Qt 中 QApplication 类提供的一个静态函数,用来恢复鼠标光标到应用程序之前设置的状态。 在 Qt 中,你可以使用 QApplication::setOverrideCursor() 来临时更改鼠标光标的外观。例如,当执行一些耗时操作时&#x…...

重生之“我打数据结构,真的假的?”--2.单链表(无习题)
C语言中的单链表总结 单链表是一种基础的数据结构,广泛应用于C语言编程中。它由节点组成,每个节点包含数据和指向下一个节点的指针。单链表的优点在于动态内存分配和高效的插入与删除操作。本文将详细探讨单链表的定义、基本操作、应用场景以及相关示例…...

【有啥问啥】视频插帧算法技术原理详解
视频插帧算法技术原理详解 引言 视频插帧(Video Interpolation)技术,作为计算机视觉领域的一项重要应用,旨在通过算法手段在已有的视频帧之间插入额外的帧,从而提升视频的帧率,使其看起来更加流畅。这一技…...
Leetcode148,109以及二者的合并 -> Tencent面试算法题 - 无序双向链表转BST
根源简述 这道题是腾讯在2024/8/30考的一道面试题,整体来说,难度不大,就是代码量稍稍有点儿大,让我们一起来看一下吧 题目描述 整数无序双向链表能否转BST(二叉搜索树),如果能,怎么转…...

【蓝桥杯选拔赛真题77】python计算小球 第十五届青少年组蓝桥杯python选拔赛真题 算法思维真题解析
目录 python计算小球 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python计算小球 第十五届蓝桥杯青少年组python比赛选拔赛真题 一、题目要…...

获取Hive表备注
DESCRIBE EXTENDED 表名;先获取Detailed Table Information这行的data_type字段数据,进行正则匹配,拿到表备注,如下: String str ReUtil.get("parameters:\\{(?!.*?\\().*transient_lastDdlTime.*?comment(.*?)\\}&quo…...
10.30学习
一、科学计数法 C语言中的科学计数法主要用于表示非常大或非常小的浮点数,它遵循以下格式: 1. E或e表示指数: 科学计数法中的E或e用来表示“指数”(Exponent)。例如, 1.23e4 或 1.23E4 表示 1.23 * 10^4…...
什么是栈溢出
一、什么是栈溢出 栈溢出(Stack Overflow)就是指在程序运行过程中,往栈里存放的数据超过了栈所能容纳的最大容量,从而导致程序出现异常行为的情况。这就好比一个箱子本来只能装一定数量的物品,硬要往里面塞更多的东西&…...
在linux中arm-linux-gcc和/usr/bin/gcc有啥区别
在Linux中,arm-linux-gcc和/usr/bin/gcc都是编译器,但它们之间存在显著的区别,主要体现在编译目标、使用场景以及编译生成的二进制文件的可执行性上。而软链接则是Linux文件系统中的一种特殊文件类型,用于创建一个文件的别名。 a…...
常用环境部署(二十二)——MySQL的数据库迁移到另一个机器上
1、导出原数据库的数据 mysqldump -u [用户名] -p[密码] [数据库名] > database_dump.sql 命令示例: mysqldump -u root -p123456 wd > /opt/wd.sql 2、在新机器上创建数据库 mysql -u [用户名] -p -e "CREATE DATABASE [新数据库名]" 命令示…...
两台主机只能单方向ping通
可能性比较大的原因时ping不通的那台主机安装了个人防火墙。 在共享上网的机器中,出于安全考虑,大部分主机都安装个人防火墙软件。几乎所有个人防火墙软件默认不允许其他机器ping本机。一般的做法是将来自外部的ICMP请求报文滤掉,对本机出去的…...

redis windows 5.0 下载
Redis 简介 Redis 是一个高性能的 key-value 数据库,广泛应用于缓存、消息队列、实时分析等场景。它支持多种数据结构,如字符串、哈希、列表、集合、有序集合等,并且提供了丰富的操作命令,能够满足各种复杂的数据处理需求。 下载…...

视频转gif怎么转换?6种视频格式转换简单方法分享,附操作截图!
gif动图凭借其简洁而生动的特点,已成为互联网交流中不可或缺的一部分。尽管gif和视频在技术上有所不同,但两者都能以短小的帧展现动作,而gif通常不带声音,具备循环播放的特性。因此,出于创建gif动图、存储更多媒体文件…...

StructRAG简介
StructRAG是一种新型的框架,旨在提升大型语言模型(LLMs)在知识密集型推理任务中的性能。它通过推理时的混合信息结构化机制,根据任务需求以最合适的格式构建和利用结构化知识。 以下是StructRAG的核心组成部分和工作流程ÿ…...
java脚手架系列12-mongoDB
之所以想写这一系列,是因为之前工作过程中有几次项目是从零开始搭建的,而且项目涉及的内容还不少。在这过程中,遇到了很多棘手的非业务问题,在不断实践过程中慢慢积累出一些基本的实践经验,认为这些与业务无关的基本的…...
python四舍五入保留两位小数
在 Python 中,你可以使用内置的 round() 函数来对数字进行四舍五入并保留两位小数。round() 函数有两个参数:要四舍五入的数字和要保留的小数位数。以下是一个简单的示例: # 示例数字 number 3.14159# 四舍五入保留两位小数 rounded_number…...

期权懂|有什么期权交易策略能够稳赚不赔的?
期权小懂小编每日分享期权知识,帮助期权新手及时有效地掌握即市趋势与新资讯! 有什么期权交易策略能够稳赚不赔的? 期权交易具有风险性,没有任何一种策略能够保证稳赚不赔。 以下是一些常见的期权交易策略,虽不能保证盈利&#…...
笔记本脱机状态
先是显示脱机,请尝试其他方法登录 1.按照联想客服,进入高级选项里面,清除两个更新项目,没有卸载成功 2.安装wepe,先是能检测到U盘,但是进不去,然后我淘宝淘帮我做盘,我自己重新装了一…...
浏览器访问 AWS ECS 上部署的 Docker 容器(监听 80 端口)
✅ 一、ECS 服务配置 Dockerfile 确保监听 80 端口 EXPOSE 80 CMD ["nginx", "-g", "daemon off;"]或 EXPOSE 80 CMD ["python3", "-m", "http.server", "80"]任务定义(Task Definition&…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...

Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...

Keil 中设置 STM32 Flash 和 RAM 地址详解
文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...