go 分布式redis锁的实现方式
go 语言以高并发著称。那么在实际的项目中 经常会用到锁的情况。比如说秒杀抢购等等场景。下面主要介绍 redis 布式锁实现的两种高并发抢购场景。其实 高并发 和 分布式锁 是一个互斥的两个状态:
方式一 setNX:
使用 redis自带的API setNX 来实现。能解决高并发场景下的 绝大多数场景,待优化点 锁的续命 和 等待锁 的实现。实现流程:
- redis setNX 设置键值。如果 键存在则返回 false 反之则为 true
- 使用 setNX 来设置一个键值,值为当前协程设置的随机值。
- 当程序运行完成之后, 删除该键值
这里只有当减库存成功:
抢购流程成功 则返回 410。其余失败则返回 200. 这样就能通过返回码 很容易看到成功抢购的数量 我么使用 postman 模拟 1600 用户点击 十分钟。库存为 一个亿。
// redis分布式锁 方式1:自己动手 // 该方案可以解决大多数场景中的 redis 锁的问题, // 还剩余一个 锁续命的问题 极高并发下的微小概率事件 func redisLock_0(c *gin.Context) {// 实现逻辑// 1 先用商品ID为 key, uuid为值, 这一步是防止别人把自己的锁删除// 2 用SetNX 设置一个键值 锁住一个商品,并设置超时时间。 当 SetNX key 存在则 返回false, 反之为 truerdb := Rdb()lockKey := "product_001"newUUID := uuid.New()// 只能删除锁 并切判断是不是自己的锁,只有自己的锁才会删除defer func() {keyValue, err := rdb.Get(ctx, lockKey).Result()if err != nil {fmt.Println("keyValue error:", keyValue, err)c.JSON(http.StatusOK, gin.H{"message": "获取锁失败",})return}if keyValue == newUUID.String() {rdb.Del(ctx, lockKey)}}()//设置锁,30秒过期,只有当锁不存在时才会成功设置,//设置时间是为了 防止特殊情况所没有成功释放。success, err := rdb.SetNX(ctx, lockKey, newUUID.String(), time.Second*30).Result()if err != nil {fmt.Println("Error setting lock: %v", err)c.JSON(http.StatusOK, gin.H{"message": "设置锁单出错",})return}// 判断是否成功获得锁if success {fmt.Println("Successfully acquired lock:", newUUID)// 执行需要锁保护的操作 获取真实的 库存count, err := strconv.Atoi(rdb.Get(ctx, "product_count").Val())if err != nil {fmt.Println("Error getting product count: %v", err)c.JSON(http.StatusOK, gin.H{"message": "Error getting product count",})return}if count > 1 {stock := count - 1err := rdb.Set(ctx, "product_count", strconv.Itoa(stock), 0).Err()if err != nil {fmt.Println("Error setting product count: %v", err)c.JSON(http.StatusOK, gin.H{"message": "Error setting product count",})return} else {fmt.Println("减库存操作成功, 现在库存为: %v", stock)c.JSON(http.StatusGone, gin.H{"message": "Hello, World!",})return}} else {fmt.Println("库存为 0 ")c.JSON(http.StatusOK, gin.H{"message": "Hello, World!",})return}} else {///没有获得锁! 可以做延迟 轮询处理fmt.Println("Failed to acquire lock. The key already exists.")c.JSON(http.StatusOK, gin.H{"message": "Hello, World!",})return} }经过十分钟我们看下数据:
该方案整体数据:
- 一共请求了 534,979 次
- 并发 877
- 成功销售 280,367 个商品 即返回值为 410的个数。
方式二 redisson:
使用 go-redisson 库,这个 类似 java redisson:
go-redisson command - github.com/paceew/go-redisson - Go Packages
https://pkg.go.dev/github.com/paceew/go-redisson
该方案使用起来就很简单了:
我们来测试一样的数据:
func redisLock_1(c *gin.Context) {//获取一个锁对象mutex := RedSon().NewMutex("godisson")//尝试加锁, 并且设置超时时间和等待时间,//如果加锁失败 会阻塞等待,或超时 或 加锁成功err := mutex.TryLock(20000, 20000)if err != nil {log.Println("can't obtained lock")c.JSON(http.StatusOK, gin.H{"message": "Error can't obtained lock",})return}defer func(mutex *godisson.Mutex) {_, err := mutex.Unlock()if err != nil {log.Println("can't obtained lock")c.JSON(http.StatusOK, gin.H{"message": "Error1 can't obtained lock",})}}(mutex)// 执行需要锁保护的操作 获取真实的 库存count, err := strconv.Atoi(rdb.Get(ctx, "product_count").Val())if err != nil {fmt.Println("Error getting product count: %v", err)c.JSON(http.StatusOK, gin.H{"message": "Error getting product count",})return}if count > 1 {stock := count - 1err := rdb.Set(ctx, "product_count", strconv.Itoa(stock), 0).Err()if err != nil {fmt.Println("Error setting product count: %v", err)c.JSON(http.StatusOK, gin.H{"message": "Error setting product count",})return} else {fmt.Println("减库存操作成功, 现在库存为: %v", stock)c.JSON(http.StatusGone, gin.H{"message": "Hello, World!",})return}} else {fmt.Println("库存为 0 ")c.JSON(http.StatusOK, gin.H{"message": "Hello, World!",})return} }
该方案整体数据:
- 一共请求 528,686
- 并发 868
- 成功销售 343,381 个商品 即返回值为 410的个数。应该是实现了锁等待。所有这个方案比自己实现的抢购 要高。
如何提高吞吐 优化性能问题
分段锁:
分段锁的核心思路就是:之前的方案都是一个锁,处理所有请求。这里呢 开十把锁。那吞吐性能不就 快了 十倍了麽。那么我们就采用redisson 来做十把分段锁:
把一个亿的商品库存,分成1千万的 十份。然后用 十把锁。这样:
func redisLock_2(c *gin.Context) {rand.Seed(time.Now().UnixNano())// 生成包含0和9的随机数num := rand.Intn(10)mutexKey := "godisson_" + strconv.Itoa(num)product_key := "product_count_" + strconv.Itoa(num)//获取一个锁对象mutex := RedSon().NewMutex(mutexKey)//尝试加锁, 并且设置超时时间和等待时间,//如果加锁失败 会阻塞等待,或超时 或 加锁成功err := mutex.TryLock(20000, 20000)if err != nil {log.Println("can't obtained lock")c.JSON(http.StatusOK, gin.H{"message": "Error can't obtained lock",})return}defer func(mutex *godisson.Mutex) {_, err := mutex.Unlock()if err != nil {log.Println("can't obtained lock")c.JSON(http.StatusOK, gin.H{"message": "Error1 can't obtained lock",})}}(mutex)// 执行需要锁保护的操作 获取真实的 库存count, err := strconv.Atoi(rdb.Get(ctx, product_key).Val())if err != nil {fmt.Println("Error getting product count: %v", err)c.JSON(http.StatusOK, gin.H{"message": "Error getting product count",})return}if count > 1 {stock := count - 1err := rdb.Set(ctx, product_key, strconv.Itoa(stock), 0).Err()if err != nil {fmt.Println("Error setting product count: %v", err)c.JSON(http.StatusOK, gin.H{"message": "Error setting product count",})return} else {fmt.Println("减库存操作成功, 现在库存为: %v", stock)c.JSON(http.StatusGone, gin.H{"message": "Hello, World!",})return}} else {fmt.Println("库存为 0 ")c.JSON(http.StatusOK, gin.H{"message": "Hello, World!",})return} }
无超卖情况:
测试结果如下:
- 一共请求 523,418
- 并发 858
- 成功销售 404,238 个商品 即返回值为 410的个数
如此看,不知道是我 单台机器性能跑满了测试不准确还是其他原因。并没有十倍的性能提升。
相关文章:
go 分布式redis锁的实现方式
go 语言以高并发著称。那么在实际的项目中 经常会用到锁的情况。比如说秒杀抢购等等场景。下面主要介绍 redis 布式锁实现的两种高并发抢购场景。其实 高并发 和 分布式锁 是一个互斥的两个状态: 方式一 setNX: 使用 redis自带的API setNX 来实现。能解决…...
Unity中Stack<T>用法以及删除Stack<GameObject>的方法
Unity中Stack用法以及删除Stack的方法 介绍Stack<T>的APIStack<T> 常用方法创建和初始化 Stack<T>Push 和 Pop 操作Stack<T>遍历清空栈检查栈是否包含某个元素 栈的典型应用场景撤销操作深度优先搜索(DFS)注意事项 总结 介绍 因…...
Vue进阶之Vue3源码解析(二)
Vue3源码解析 运行runtime-coresrc/createApp.tssrc/vnode.ts.tssrc/renderer.ts runtime-domsrc/index.ts 总结 运行 runtime-core src/createApp.ts vue的创建入口 import { createVNode } from "./vnode";export function createAppAPI(render) {return funct…...
linux的文件系统及文件类型
目录 一、Linux支持的文件系统 二、linux的文件类型 2.1、普通文件 2.2、目录文件 2.3、链接文件 2.4、字符设备文件: 2.5、块设备文件 2.6、套接字文件 2.7、管道文件 三、linux的文件属性 3.1、关于权限部分 四、Linux的文件结构 五、用户主目录 5.1、工作目录…...
如何下载安装 PyCharm?
李升伟 整理 一、下载 PyCharm 访问官网 打开 PyCharm 官网,点击 "Download" 按钮25。 版本选择: 社区版(Community):免费使用,适合个人学习和基础开发。 专业版(Professional&#…...
3D空间曲线批量散点化软件V1.0正式发布,将空间线条导出坐标点,SolidWorks/UG/Catia等三维软件通用
软件下载地址: SolidWorks/UG/Catia等三维软件通用,3D空间曲线批量散点化软件V1.0正式发布,将空间线条导出坐标点 - 陶小桃Blog在三维设计领域,工程师常需将复杂空间曲线转化为离散坐标点以用于逆向工程、有限元分析、数控加工或…...
WPS AI+office-ai的安装、使用
** 说明:WPS AI和OfficeAI是两个独立的AI助手,下面分别简单讲下如何使用 ** WPS AI WPS AI是WPS自带AI工具 打开新版WPS,新建文档后就可以看到菜单栏多了一个“WPS AI”菜单,点击该菜单,发现下方出现很多菜单…...
java后端开发day27--常用API(二)正则表达式爬虫
(以下内容全部来自上述课程) 1.正则表达式(regex) 可以校验字符串是否满足一定的规则,并用来校验数据格式的合法性。 1.作用 校验字符串是否满足规则在一段文本中查找满足要求的内容 2.内容定义 ps:一…...
拼电商客户管理系统
内容来自:尚硅谷 难度:easy 目 标 l 模拟实现一个基于文本界面的 《 拼电商客户管理系统 》 l 进一步掌握编程技巧和调试技巧,熟悉面向对象编程 l 主要涉及以下知识点: 类结构的使用:属性、方法及构造器 对象的创建与…...
华为:Wireshark的OSPF抓包分析过程
一、OSPF 的5包7状态 5个数据包 1.Hello:发现、建立邻居(邻接)关系、维持、周期保活;存在全网唯一的RID,使用IP地址表示 2.DBD:本地的数据库的目录(摘要),LSDB的目录&…...
Android项目优化同步速度
最近项目需要使用ffmpeg,需要gradle配置引入ffmpeg库,发现原来通过google官方的代码仓,下载太慢了,每秒KB级别的速度。(之前下gradle/gradle plugin都不至于这么慢),于是想到配置国内镜像源来提…...
在线教育网站项目第二步 :学习roncoo-education,服务器为ubuntu22.04.05
一、说明 前端技术体系:Vue3 Nuxt3 Vite5 Vue-Router Element-Plus Pinia Axios 后端技术体系:Spring Cloud Alibaba2021 MySQL8 Nacos Seata Mybatis Druid redis 后端系统:roncoo-education(核心框架:S…...
STM32-GPIO详解
目录 一:GPIO基本概念 编辑 二:GPIO的实际应用 三:功能描述 四:GPIO库函数 五:寄存器 GPIO相关寄存器功能 一:GPIO基本概念 GPIO是英文General Purpose Input/Output的缩写,中文翻译为…...
【Framework系列之Client】UIManager和UIPanel模块介绍
今天来介绍一下UIManager和UIPanel模块,话不多说直接开始。 UIManager 功能介绍 UIManager是管理UIPanel的唯一模块,UIManager的主要功能包括: 提供打开、隐藏、关闭UIPanel的相关接口。负责UIPanel对象的创建以及初始化。负责储存UIPanel对…...
阿里云操作系统控制台——ECS操作与性能优化
引言:在数字化时代,云服务器作为强大的计算资源承载平台,为企业和开发者提供了灵活且高效的服务。本文将详细介绍如何一步步操作云服务器 ECS,从开通到组件安装,再到内存全景诊断,帮助快速上手,…...
【长安大学】苹果手机/平板自动连接认证CHD-WIFI脚本(快捷指令)
背景: 已经用这个脚本的记得设置Wifi时候,关闭“自动登录” 前几天实在忍受不了CHD-WIFI动不动就断开,一天要重新连接,点登陆好几次。试了下在网上搜有没有CHD-WIFI的自动连接WIFI自动认证脚本,那样我就可以解放双手&…...
第51天:Web开发-JavaEE应用SpringBoot栈身份验证JWT令牌Security鉴权安全绕过
#知识点 1、安全开发-JavaEE-身份验证-JWT&Security 2、安全开发-JavaEE-安全问题-不安全写法&版本漏洞 #开发框架-SpringBoot 参考:Spring Boot 中文文档 一、身份验证的常见技术: 1、JWT 2、Shiro 3、Spring Security 4、OAuth 2.0 5、SSO 6、…...
中原银行:从“小机+传统数据库”升级为“OceanBase+通用服务器”,30 +系统成功上线|OceanBase DB大咖说(十五)
OceanBase《DB 大咖说》第 15 期,我们邀请到了中原银行金融科技部数据团队负责人,吕春雷。本文为本期大咖说的精选。 吕春雷是一位资历深厚的数据库专家,从传统制造企业、IT企业、甲骨文公司到中原银行,他在数据库技术与运维管理…...
Java面试第八山!《Spring框架》
一、Spring框架概述 Spring是Java企业级应用开发的核心框架,通过控制反转(IoC)和 面向切面编程(AOP)实现模块解耦,简化开发流程。其核心优势包括依赖注入、声明式事务管理、集成主流ORM框架(如…...
LangChain教程 - Agent - 支持 9 种 ReAct 交互
引言 LangChain 总结了 9 种经典的复杂模型交互模式,每种都针对特定任务设计,兼具独特优势与适用场景,内容涵盖: ReAct、Function Call、知识库、搜索等,使用这些模式可以大大简化这些场景开发难度。这些模式可以使用…...
基于服务器使用 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…...
UE5 学习系列(三)创建和移动物体
这篇博客是该系列的第三篇,是在之前两篇博客的基础上展开,主要介绍如何在操作界面中创建和拖动物体,这篇博客跟随的视频链接如下: B 站视频:s03-创建和移动物体 如果你不打算开之前的博客并且对UE5 比较熟的话按照以…...
C# SqlSugar:依赖注入与仓储模式实践
C# SqlSugar:依赖注入与仓储模式实践 在 C# 的应用开发中,数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护,许多开发者会选择成熟的 ORM(对象关系映射)框架,SqlSugar 就是其中备受…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...
Kubernetes 节点自动伸缩(Cluster Autoscaler)原理与实践
在 Kubernetes 集群中,如何在保障应用高可用的同时有效地管理资源,一直是运维人员和开发者关注的重点。随着微服务架构的普及,集群内各个服务的负载波动日趋明显,传统的手动扩缩容方式已无法满足实时性和弹性需求。 Cluster Auto…...
若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...
【Vue】scoped+组件通信+props校验
【scoped作用及原理】 【作用】 默认写在组件中style的样式会全局生效, 因此很容易造成多个组件之间的样式冲突问题 故而可以给组件加上scoped 属性, 令样式只作用于当前组件的标签 作用:防止不同vue组件样式污染 【原理】 给组件加上scoped 属性后…...
基于 Transformer robert的情感分类任务实践总结之二——R-Drop
基于 Transformer robert的情感分类任务实践总结之一 核心改进点 1. R-Drop正则化 原理:通过在同一个输入上两次前向传播(利用Dropout的随机性),强制模型对相同输入生成相似的输出分布,避免过拟合。实现:…...
JSON解析崩溃原因及解决方案
问题记录: /************************************************| * 描述: 将ID124执行NFC操作-JSON解析为结构体* 函数名: cJSON_ID124_to_struct* 参数[ I]: *json_string 待解析的指针* 参数[II]: *wireless_rxd 结构体指针* 返回: 成功返回0 失…...
基于SpringBoot实现的大创管理系统设计与实现【源码+文档】
基于SpringBootVue实现的大创管理系统采用前后端分离架构方式,系统设计了管理员、学生、指导老师、院系管理员两种角色,系统实现了用户登录与注册、个人中心、学生管理、指导老师管理、院系管理员管理、优秀项目管理、项目类型管理、项目信息管理、项目申…...









