职业生涯第一课---“Redis分布式锁优化:确保唯一性与效率“

前言

最近因为刚入职公司开启自己的实习生涯,工作和毕设论文同步进行,导致有段时间没更新博客了,今天来分享一下最近学到的一些知识。

场景介绍

BOSS让我写一些接口,他提出这样一个需求,该接口的参数有多个,其中包含shopname参数,该参数要根据调用者传入的shopname再在后边拼接一个id,作为一个新的字段来作为参数去调用别的接口。而且要保证ID的唯一性。因为BOSS要我写的接口是我们公司内部用的系统的接口,并发量不会太大,但我想万一真有两个人近乎一起用了该接口,该怎样保证获取的id的唯一性。于是就有了下面的经历。

该场景需要从Redis中获取一个唯一id并修改它的值,因为要保整其获取的id的唯一性,又因有一定的并发量,所以采用先占位再获取的方法,防止id重复。

待优化代码

这段代码是我本来自己写的,后来感觉质量不是太高,总觉得能有优化的地方,但由于公司就我一个Python,没办法去问别人,于是就去问ChatGPT。源代码如下。

# 获取当前id_flag,采取先占位再获取的方法,防止id重复
def get_curentid(report_num:int) -> int:def get_titleid_flag() -> int:titleid_flag = int(r.get("titleid_flag"))return titleid_flagdef set_titleid_flag(old_titleid_flag, new_titleid_flag: int) -> bool:if old_titleid_flag == get_titleid_flag():r.set("titleid_flag", new_titleid_flag)return Trueelse:return Falsetime_pull = time.time()time_end = time_pull + 5old_titleid_flag = get_titleid_flag()while True:if set_titleid_flag(old_titleid_flag, old_titleid_flag + report_num):return old_titleid_flagelif time.time() < time_end:old_titleid_flag = get_titleid_flag()continueelse:return -1

代码解释:

该函数是用来当前 id_flag 的方法。采用了先占位再获取的策略,以防止 id 的重复使用。下面是简单的解释:

  1. get_curentid(report_num:int) -> int: 这是主函数,它接受一个整数参数 report_num,表示要修改的id值,并返回一个整数作为当前的 id_flag。
  2. get_titleid_flag() -> int: 这个函数从Redis中获取当前的 titleid_flag,并将其转换为整数后返回。
  3. set_titleid_flag(old_titleid_flag, new_titleid_flag: int) -> bool: 这个函数尝试将 titleid_flag 的值从旧值更新为新值。在更新前先查询旧值有没有变化,若无变化则更新。如果更新成功,则返回 True,否则返回 False。
  4. 在主函数中通过循环不断尝试更新 titleid_flag 的值,直到更新成功或者超时时间到达。超时时间为 5 秒。
  5. 如果成功更新了 titleid_flag 的值,则返回旧的 titleid_flag,表示成功获取到了 id_flag。如果超时未能成功更新,则返回 -1,表示获取失败。

总之,这个函数通过Redis中的一个标识值来确保获取到的 id_flag 是唯一的,并且采用了一定的重试机制来应对可能的竞争条件或者网络延迟导致的更新失败情况。

然后问了ChatGPT后,他给了我一下优化建议。

代码可优化方向:

image.png

因为对原子操作和分布式锁之前没了解过,所以特意去搜索了解了一下。

原子操作介绍:

image.png

分布式锁介绍

image.png

一些好的提高并发度和性能的算法或方案

之后又问了ChatGPT有没有什么好的方案,他给出了一下方案,总结一下就是;确保访问唯一性,减少访问频次,限制并发数量。具体回答如下:

image.png

令牌桶算法和漏桶算法

除此之外,还了解了两个新的关于限流的算法。
image.png

优化后的代码

基于上述信息,我对原本的代码进行了下列优化,利用Redis的原子操作来优化这段代码,使用 Redis 的 SETNX(SET if Not eXists)命令来实现分布式锁。SETNX 命令可以在 key 不存在的情况下设置 key 的值,如果 key 已经存在,则不进行任何操作。

import redis
import time# 创建 Redis 客户端连接
redis_client = redis.Redis(host='localhost', port=6379, db=0)# 获取铺位评估报告当前 id_flag,采取先占位再获取的方法,防止id重复
def get_curentid(report_num: int) -> int:# 设置锁的过期时间,防止锁未正常释放导致死锁lock_timeout = 5  # 5秒# 生成锁的键名lock_key = "titleid_flag_lock"# 初始化等待时间wait_time = 0.1# 循环尝试获取锁while True:# 使用 SETNX 命令尝试获取锁lock_acquired = redis_client.setnx(lock_key, "locked")if lock_acquired:# 如果成功获取锁,则设置锁的过期时间redis_client.expire(lock_key, lock_timeout)# 获取当前 id_flagcurrent_id_flag = int(redis_client.get("titleid_flag") or 0)new_id_flag = current_id_flag + report_numredis_client.set("titleid_flag",new_id_flag)# 释放锁redis_client.delete(lock_key)return current_id_flagelse:# 如果获取锁失败,则等待一段时间后重试time.sleep(wait_time)# 等待时间指数增加wait_time *= 2  # 指数增长,可以根据实际情况调整return -1  # 获取失败时返回 -1# 测试代码
report_num = 10
current_id = get_curentid(report_num)
print("Current ID Flag:", current_id)

在这个优化版本中,通过使用 Redis 的 SETNX 命令来获取分布式锁,避免了之前循环重试的方式,提高了效率。同时,在获取锁成功后,设置了锁的过期时间,以防止锁未正常释放导致的死锁问题。同时为了引入指数退避策略,可以在获取锁失败后进行等待时间的指数增加。这样可以减少频繁重试锁的获取,降低系统负载,提高效率。

但是由于只是在开发环境下测试,也没办法模拟高并发情况来对比两段代码的运行结果,理论上优化后的是由优于前者的,但本地测试感官上是差不多。没人带真难受,老代码是我入职第一天晚上自己要的,服务器宝塔面板是第二天自己主动要的,Redis密码是自己在配置文件里查的,遇到问题也是自己解决的,我这两排工位没一个同事,感觉赛博孤岛似的,

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/706295.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Windows下配置TortoiseGit 访问Ubuntu虚拟机下Samba共享目录

前言&#xff1a; 本文记录学习使用 Git 版本管理工具的学习笔记&#xff0c;通过阅读参考链接中的博文和实际操作&#xff0c;快速的上手使用 Git 工具。 本文参考了引用链接博文里的内容。 引用: 【TortoiseGit】TortoiseGit安装和配置详细说明-CSDN博客 Git版本管理可视…

ubuntu下安装pwndbg

安装pwndbg 如果可以科学上网 首先安装git apt install git 然后拉取git库 git clone GitHub - pwndbg/pwndbg: Exploit Development and Reverse Engineering with GDB Made Easy 进入到pwngdb的文件夹中 cd pwngdb 执行 ./setup.sh 而后输入gdb 出现红色pwndgb就是安装成功…

ALV 图标显示

前言 在ABAP ALV中&#xff0c;使用fieldcat来定义列表中每个字段的显示属性&#xff0c;包括图标&#xff08;Icon&#xff09;的显示。图标可以在ALV列表中为特定列的行或标题添加图形元素&#xff0c;以增强视觉提示或传达附加信息。 ICON查询 图标的名称用事务码”ICON“进…

析构函数详解

目录 析构函数概念特性对象的销毁顺序 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 &#x1f412;&#x1f412;&#x1f412; 个人主页 &#x1f978;&#x1f978;&#x1f978; C语言 &#x1f43f;️&#x1f43f;️&#x1f43f;️ C语言例题 &…

设计模式与软件体系结构课后练习参考答案

目录 软件设计模式第二章 创建型软件设计模式1. 工厂模式2. 生成器模式3. 单例模式 第三章 结构型软件设计模式1. 组合模式2. 适配器模式3. 外观模式4. 桥接模式 第四章 行为型软件设计模式1. 迭代器模式2. 访问者模式3. 中介者模式4. 策略模式5. 状态模式 案例分析工厂模式案例…

LED电源质量和性能测试解析

LED电源的性能对于确保照明系统的稳定性和效率至关重要。在LED技术不断进步的今天&#xff0c;对电源进行严格的测试成为了一项挑战。本文将详细探讨LED电源的测试项目&#xff0c;包括电性能、保护功能和安规测试&#xff0c;以及可靠性测试&#xff0c;旨在为测试员提供一个全…

Springboot开发 -- Postman 调试 session 验证 接口

当我们在开发Spring Boot应用时&#xff0c;经常会遇到带有Session验证的接口&#xff0c;这些接口需要用户先登录并获取到Session ID&#xff08;或称为cookie中的JSESSIONID&#xff09;&#xff0c;然后在后续的请求中携带这个Session ID来保持会话状态。下面我将以一个实际…

6---Linux下版本控制器Git的知识点

一、Linux之父与Git的故事&#xff1a; Linux之父叫做“Linus Torvalds”&#xff0c;我们简称为雷纳斯。Linux是开源项目&#xff0c;所以在Linux的早期开发中&#xff0c;许多世界各地的能力各异的程序员都参与到Linux的项目开发中。那时&#xff0c;雷纳斯每天都会收到许许…

STM32HAL库-中断篇

中断 中断简介 中断是一种事件处理机制&#xff0c;可以暂停主程序的运行&#xff0c;转而处理特定事件程序。 中断的作用和意义&#xff1a; 实时控制 在确定事件内对响应事件做出相应 故障处理 检测到故障需要第一时间处理 数据传输 如串口通信&#xff0c;不确定数…

大模型对数据库运维的赋能:智能运维的新时代

引言随着人工智能技术的飞速发展&#xff0c;大模型作为AI领域的前沿技术&#xff0c;已经开始在数据库运维(DBA)领域展现出其独特的价值。大模型的引入&#xff0c;不仅提升了数据库运维的效率&#xff0c;还极大地改善了运维的质量和智能化水平。本文将深入分析大模型在数据库…

Python专题:十五、JSON数据格式

Python的数据处理&#xff1a;JOSN 计算机的主要工作&#xff1a;处理数据 最容易处理的数据就是结构化数据 非结构化数据&#xff1a;视频&#xff0c;文件等 近些年的大数据、数据挖掘就是对互联网中的各种非结构化的数据的分析和处理 半结构化数据 明确的结构属性&…

开发属于自己的Spring Boot Starter-18

为什么要开发专用的Spring Boot Starter Spring在通常使用时&#xff0c;一般是通过pom.xml文件中引入相关的jar包&#xff0c;然后再通过application.yml文件配置初始化bean的配置&#xff0c;但随着项目越来越复杂或是项目组中的应用数量越来越多&#xff0c;可能会带来几个…