职业生涯第一课---“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_flag

    def 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 True
        else:
            return False

    time_pull = time.time()
    time_end = time_pull + 5
    
    old_titleid_flag = get_titleid_flag()
    while True:
        if set_titleid_flag(old_titleid_flag, old_titleid_flag + report_num):
            return old_titleid_flag
        elif time.time() < time_end:
            old_titleid_flag = get_titleid_flag()
            continue
        else:
            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_flag
            current_id_flag = int(redis_client.get("titleid_flag") or 0)
            new_id_flag = current_id_flag + report_num
            redis_client.set("titleid_flag",new_id_flag)

            # 释放锁
            redis_client.delete(lock_key)

            return current_id_flag
        else:
            # 如果获取锁失败,则等待一段时间后重试
            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密码是自己在配置文件里查的,遇到问题也是自己解决的,我这两排工位没一个同事,感觉赛博孤岛似的,

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

linux系统查看CPU信息

1、查看cpu型号 [rootMaster ~]# cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c 40。Intel(R) Xeon(R) CPU E5-2650 v3 2.30GHz 2、查看系统中实际物理CPU的颗数&#xff08;物理&#xff09; [rootMaster ~]# grep physical id /proc/cpuinfo | sort | uniq | w…

IT行业现状与探索未来发展趋势

​​​​​​​ 我眼中的IT行业现状与未来趋势 随着技术的不断进步&#xff0c;IT行业已成为推动全球经济和社会发展的关键力量。从云计算、大数据、人工智能到物联网、5G通信和区块链&#xff0c;这些技术正在重塑我们的生活和工作方式。你眼中IT行业的现状及未来发展趋势是…

Python函数之旅专栏(导航)

Python内置函数(参考版本:3.11.8)AELRabs( )enumerate( )len( )range( )aiter( )eval( )list( )repr( )all( )exec( )locals( )reversed( )anext( )round( )any( ) ascii( )FM  filter( )map( )S float( )max( )set( )Bformat( )memoryview( )setattr( )bin( )frozenset( )…

Spring实现数据库读写分离(MySQL实现主从复制)

目录 1、背景 2、方案 2.1 应用层解决: 2.2 中间件解决 3、使用Spring基于应用层实现 3.1 原理 3.2 DynamicDataSource 3.3 DynamicDataSourceHolder 3.4 DataSourceAspect 3.5 配置2个数据源 3.5.1 jdbc.properties 3.5.2 定义连接池 3.5.2 定义DataSource 3.6…

【Linux】线程周边001之多线程

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.线程的理解 2.地址…

停车场车位引导管理系统工作原理是什么,由哪些软硬件设备组成?

在现代城市中&#xff0c;随着汽车保有量的持续增长&#xff0c;停车难成为了许多城市面临的共同问题。有效管理停车场资源&#xff0c;提高车位利用率&#xff0c;减少寻找停车位的时间&#xff0c;对于缓解交通拥堵、提高城市运行效率具有重要意义。车位引导管理系统正是为了…

YOLOv8改进 | 图像修复 | 适用多种复杂场景的全能图像修复网络AirNet助力YOLOv8检测(全网独家首发)

一、本文介绍 本文给大家带来的改进机制是一种适用多种复杂场景的全能图像修复网络AirNet&#xff0c;其由对比基降解编码器&#xff08;CBDE&#xff09;和降解引导修复网络&#xff08;DGRN&#xff09;两个神经模块组成&#xff0c;能够在未知损坏类型和程度的情况下恢复受…

Java | Leetcode Java题解之第91题解码方法

题目&#xff1a; 题解&#xff1a; class Solution {public int numDecodings(String s) {int n s.length();// a f[i-2], b f[i-1], cf[i]int a 0, b 1, c 0;for (int i 1; i < n; i) {c 0;if (s.charAt(i - 1) ! 0) {c b;}if (i > 1 && s.charAt(i …

主流短视频评论采集python爬虫(含一二级评论内容)

声明 仅用于学习交流&#xff0c;不用于其他用途 正文 随着主流短视频评论采集更新需要登录&#xff0c;由于不懈的努力&#xff0c;攻破这一难点&#xff0c;不需要登录采集作品所有评论信息 话不多说上代码看效果&#xff1a; 输入作品id: 这样就拿到评论信息了&#xff…

小程序|锁定查询功能如何使用?

学生或家长想要实现自己查询完成后&#xff0c;任何人都无法再次查询&#xff0c;老师应该如何设置&#xff1f;易查分的【锁定查询功能】就可实现&#xff0c;下面教大家如何使用吧。 &#x1f4cc;使用教程 &#x1f512;锁定查询功能介绍 ✅学生或家长自主锁定&#xff1a;开…

webpack优化构建体积示例-并行压缩:

uglifyjs-webpack-plugin和terser-webpack-plugin都可以开启多进程并进行压缩来减小构件体积大小。 当在 Webpack 配置中启用 minimize: true 时&#xff0c;构建时间通常会增加&#xff0c;这是因为 Webpack 会在构建过程中添加一个额外的步骤&#xff1a;代码压缩。代码压缩是…

分布式搜索——ElasticSeach简介

一般都用数据库存储数据&#xff0c;然后对数据库进行查询获取数据&#xff0c;但是当数据量很大时&#xff0c;查询效率就会很慢&#xff08;具体下面会讲到&#xff09;&#xff0c;所以这种情况下就会使用到ElasticSeach ElasticSeach的基本介绍 ElasticSeach是一 款非常强…

202012青少年软件编程(Python)等级考试试卷(三级)

第 1 题 【单选题】 在Python正则表达式中&#xff0c;用来匹配任意空白字符的是&#xff08; &#xff09;。 A &#x1f612; B :S C :d D &#x1f604; 正确答案:A 试题解析: 第 2 题 【单选题】 在Python正则表达式中&#xff0c;用来匹配任意非数字字符的是&…

【神经网络与深度学习】Transformer原理

transformer ENCODER 输入部分 对拆分后的语句x [batch_size, seq_len]进行以下操作 Embedding 将离散的输入&#xff08;如单词索引或其他类别特征&#xff09;转换为稠密的实数向量&#xff0c;以便可以在神经网络中使用。位置编码 与RNN相比&#xff0c;RNN是一个字一个字…

代码随想录——二叉树的最小深度(Leetcode111)

题目链接 层序遍历 遍历整棵树&#xff0c;当找到一个叶子节点时&#xff0c;直接返回这个叶子节点的深度。 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNod…

C语言 | Leetcode C语言题解之第86题分隔链表

题目&#xff1a; 题解&#xff1a; struct ListNode* partition(struct ListNode* head, int x) {struct ListNode* small malloc(sizeof(struct ListNode));struct ListNode* smallHead small;struct ListNode* large malloc(sizeof(struct ListNode));struct ListNode* …

ELF 1技术贴|如何在Ubuntu上配置Samba服务器

Samba是一个开源的软件套件&#xff0c;提供了一种实现SMB/CIFS协议的方式&#xff0c;可以无缝链接Linux与Windows系统&#xff0c;让开发者在局域网络框架下实现共享文件、打印资源等&#xff0c;确保了数据交流的高效与稳定。 相较于在Ubuntu环境下运用传统的Vim编辑器&…

Wiley数据库文献哪里比较全?去哪里下载比较高效

Wiley出版社1807年创建于美国&#xff0c;是一家具有超过200年历史的全球知名的出版机构&#xff0c;面向专业人士、科研人员、教育工作者、学生、终身学习者提供必需的知识和服务。 Wiley及旗下的子品牌出版了超过500位诺贝尔奖得主的作品。Wiley Online Library为全学科期刊全…

VMware17虚拟机安装Kali Linux2024详解

目录 简介 一、环境搭建 二、下载ISO镜像 三、新建虚拟机 为虚拟机选择合适的操作系统类型和版本 分配适当的内存、硬盘空间和其他虚拟机配置选项 四、硬件配置 编辑虚拟机设置 选择安装介质 五、界面化安装配置 简介 Kali Linux是一个基于Debian的Linux发行版&#…

【Pytorch】9.torch.nn.MaxPool2d

什么是MaxPool2d 是对二维矩阵进行池化层下采样的方法 MaxPool2d的用法 相较于卷积层&#xff0c;多出来的参数为ceil_mode 这个参数代表&#xff0c;如果所剩的部分不够卷积核的大小&#xff0c;要不要进行池化操作 具体代码为 import torch import torchvision from torch …