不懂分布式锁的进来看看吧

SIGIR 2026:信息检索前沿技术与投稿指南 信息检索(Information Retrieval, IR)是计算机科学中处理大规模数据检索的核心技术,其基本原理是通过算法模型从海量数据中高效提取相关信息。随着深度学习的发展,神经信息检索(Neural IR)和多模态检索成为研究热点,如稠密检索模型(DPR)和跨模态预训练(CLIP)等技术的应用显著提升了检索精度。这些技术不仅在学术领域具有重要价值,还在电商搜索、医疗检索等实际场景中展现出巨大潜力。SIGIR作为信息检索领域的顶级会议,汇聚了全球顶尖学者和工业界专家,是展示最新研究成果的重要平台。本文 阅读详情

简介

我们常会遇到某段逻辑在相同时间段内只希望被单个实例执行,而在微服务架构中,一个程序可能会存在多个实例,此时就需要通过分布式锁来实现串行执行。

最简单的分布式锁无非就是找到对于多个程序实例而言单一的存在,比如MySQL数据只有一个或Redis只有一个,此时都可以利用这单一的存在构建一个锁,多个程序实例要执行某段逻辑前必须先获得这个锁,然后才能执行。

因为某些原因,上班的时候我和同事一起研究了一下Python redis库中分布式锁的实现源码,这里简单分享一下。

通过pip可以安装这个库。

pip install redis==2.10.6

这里以这个库的2.10.6版本为例,对它Redis分布式锁源码进行简单的分析。

码字不易废话两句:有需要python学习资料的或者有技术问题交流 “点击”即可

代码分析

实例化StrictRedis对象后,使用其中的lock方法便可获得一个分布式锁。

首先看一下lock方法对应的源码。

def lock(self, name, timeout=None, sleep=0.1, blocking_timeout=None,
             lock_class=None, thread_local=True):
        if lock_class is None:
            if self._use_lua_lock is None:
                # the first time .lock() is called, determine if we can use
                # Lua by attempting to register the necessary scripts
                try:
                    LuaLock.register_scripts(self)
                    self._use_lua_lock = True
                except ResponseError:
                    self._use_lua_lock = False
            lock_class = self._use_lua_lock and LuaLock or Lock
        return lock_class(self, name, timeout=timeout, sleep=sleep,
                          blocking_timeout=blocking_timeout,
                          thread_local=thread_local)

该方法提供了多个参数,其中:

name用于指定锁名

timeout用于指定锁的超时时间

sleep用于指定线程睡眠时间,线程争夺锁的过程本质就是一个循环,每过sleep秒,就会尝试去获取锁对象

blocking_timeout用于指定阻塞超时时间,当多个实例争夺锁时,这个时间就是实例等待锁的最长时间

lock_class表示使用锁的类对象

thread_local表示是否线程安全

方法中最关键的一句代码为lock_class = self._use_lua_lock and LuaLock or Lock,确定了lock_class后,便实例化该lock_class即可。

lock_class可以为LuaLock也可为Lock,经过简单分析,Lock类才是关键,LuaLock类继承自Lock,通过Lua代码实现Redis的一些操作,这里着重看Lock类。

首先看到该类的__init__方法。

class Lock(object):
    def __init__(self, redis, name, timeout=None, sleep=0.1,
                 blocking=True, blocking_timeout=None, thread_local=True):
        self.redis = redis
        self.name = name
        self.timeout = timeout
        self.sleep = sleep
        self.blocking = blocking
        self.blocking_timeout = blocking_timeout
        self.thread_local = bool(thread_local)
        self.local = threading.local() if self.thread_local else dummy()
        self.local.token = None
        if self.timeout and self.sleep > self.timeout:
            raise LockError("'sleep' must be less than 'timeout'")

__init__方法初始化不同的属性,其中self.local为线程的本地字段,用于存储该线程特有的数据,不与其他线程进行共享。

此外,在__init__方法中对timeout与sleep进行的判断,如果线程等待锁时的睡眠时间大于锁的超时时间,则直接返回错误。

接着重点看Lock类中的acquire方法,该方法代码如下。

import time as mod_time

class Lock(object):

    def acquire(self, blocking=None, blocking_timeout=None):
        sleep = self.sleep
        token = b(uuid.uuid1().hex)
        if blocking is None:
            blocking = self.blocking
        if blocking_timeout is None:
            blocking_timeout = self.blocking_timeout
        stop_trying_at = None
        if blocking_timeout is not None:
            stop_trying_at = mod_time.time() + blocking_timeout
        while 1:
            if self.do_acquire(token):
                self.local.token = token
                return True
            if not blocking:
                return False
            if stop_trying_at is not None and mod_time.time() > stop_trying_at:
                return False
            mod_time.sleep(sleep)

acquire方法的主逻辑就是一个死循环,在死循环中调用do_acquire方法获取Redis分布式锁,如果成功获得锁,则将token存储到当前线程的local对象中,如果没有获得,则判断blocking,如果blocking为Flase,则不再阻塞,直接返回结果,反之,则判断当前时间是否超过blocking_timeout,超过,同样返回False,反之,通过sleep方法让当前线程睡眠sleep秒。

进一步分析do_acquire方法,代码如下:

  def do_acquire(self, token):
        if self.redis.setnx(self.name, token):
            if self.timeout:
                # convert to milliseconds
                timeout = int(self.timeout * 1000) # 转成毫秒
                self.redis.pexpire(self.name, timeout)
            return True
        return False

do_acquire方法中,一开始通过redis的setnx方法将name对着作为key,token作为value,setnx方法只有在key不存的情况下,才能正常的将value存入Redis中,若key依存,该方法不做任何操作,此时就相当于没有获取到锁。

将token成功插入后,则判断有无超时时间,如果设置了timeout,则通过pexpire方法将redis中name这个key的超时设置一下,因为pexpire方法是以毫秒为单位的,所以需要先将timeout转换成毫秒单位。

如果没有设置timeout,那么name这个key只能通过do_release方法中的逻辑清除。

至此,我们清楚的知道了,Redis分布式锁的本质其实就是Redis中的一个key-value,非常简单…

理清锁的获取逻辑后,来看一下相应的释放逻辑,主要关注release方法,该方法代码如下。

def release(self):
        "Releases the already acquired lock"
        expected_token = self.local.token
        if expected_token is None:
            raise LockError("Cannot release an unlocked lock")
        self.local.token = None
        self.do_release(expected_token)

release方法中先将线程中的token取出,并将其置为None,然后调用do_release方法实现锁的释放,do_release方法代码如下。

def do_release(self, expected_token):
        name = self.name

        def execute_release(pipe):
            lock_value = pipe.get(name)
            if lock_value != expected_token:
                raise LockError("Cannot release a lock that's no longer owned")
            pipe.delete(name)

        self.redis.transaction(execute_release, name)

do_release方法的逻辑非常简单,其主要逻辑在execute_release方法,通过Redis的transaction方法开启一个事务来执行execute_release方法中逻辑。

在execute_release中,首先通过get方法获取name这个key对应的value,获得后,通过delete方法将其删除,实现Redis分布式锁的释放。

blocking属性

观察到acquire方法的这段代码。

 while 1:
            if self.do_acquire(token):
                self.local.token = token
                return True
            if not blocking:
                return False
            if stop_trying_at is not None and mod_time.time() > stop_trying_at:
                return False
            mod_time.sleep(sleep)

如果blocking为True,获取不到锁,则执行后面的逻辑,让线程睡眠,阻塞等待其他线程将锁释放;如果blocking为False,获取不到锁,则直接返回获取锁失败。

这就会引出几种情况,假设现在有线程A与线程B都需要执行相同的逻辑,执行前需要获取锁。

如果线程A在执行的过程中,线程B也要执行了,如果blocking为True,此时线程B会被阻塞,等待线程A是否Redis锁;如果blocking为False,线程B此时获取不到锁,不执行相同的逻辑。

如果线程A执行完了,此时线程B到来,如果blocking为True或False,此时线程B都不会被阻塞并成功拿到锁,执行相同的逻辑。

一个简单的结论是,blocking无法保证逻辑是否被单次执行,如果希望通过Redis分布式锁让逻辑只执行一次,依旧需要从业务层面做控制,比如MySQL中的业务数据是否被修改或Redis中是否记录这业务数据等。

以上就是小编今天为大家带来的内容,小编本身就是一名python开发工程师,我自己花了三天时间整理了一套python学习教程,从最基础的python脚本到web开发,爬虫,数据分析,数据可视化,机器学习,等,这些资料有想要的小伙伴 " 点击 " 即可领取

PADS VX2.4差分对设置避坑指南:常见错误与解决方案 本文深度解析了在PADS VX2.4中进行高速PCB设计时,差分对设置的核心要点与常见陷阱。针对Layout规则设置、Router布线实战中的阻抗控制、线长匹配等关键环节,提供了具体的避坑指南和解决方案,帮助工程师确保信号完整性,提升设计质量。 阅读详情

相关推荐

UVM实战 卷I学习笔记1——简单的UVM验证平台:只有driver

目录验证平台的组成最简单的验证平台——只有driverUVM如何搭建driver:加入factory机制加入objection机制加入virtual interface 个人《UVM实战卷I》学习随手笔记,此书作者:张强 验证平台的组成 一个验证平台要实现的基本功能: 模拟DUT的各种真实使用情况——给DUT施加各种激励→driver实现激励功能; 根据DUT的输出判断DUT行为是否与预期符合→scoreboard(也称为checker)——需注意判断的东西和判断的标准是什么; 收集DUT的输出并传递

weixin_41979380的博客 3889

Redis分布式锁简单分析

简介我们常会遇到某段逻辑在相同时间段内只希望被单个实例执行,而在微服务架构中,一个程序可能会存在多个实例,此时就需要通过分布式锁来实现串行执行。最简单的分布式锁无非就是找到对于多个程序实...

weixin_30230009的博客 547

脑机接口(BCI)从原理到开发实战 10|脑控外设:用脑电波控制机械臂/游戏

摘要: 脑机接口(BCI)的“最后一公里”是将分类结果转化为实际动作的关键环节。本文介绍如何通过指令映射、硬件通信和执行反馈实现脑控外设,包括离散/连续控制策略、防抖处理及串口/蓝牙通信方法。通过两个实战案例(运动想象控制机械臂抓取、P300/SSVEP控制游戏)和完整项目(OpenBCI控制小车),演示了从分类标签到设备动作的完整流程,重点涵盖指令生成算法(多数投票)、Python与Arduino的串口通信集成,为BCI系统落地提供实用解决方案。(149字)

小明的博客 763

基于 Redis 构建简单分布式锁的局限

简介业务中,常有分布式锁的需求,常见的解决方案便是基于 Redis 作为中心节点实现伪分布式效果,因为存在中心节点,所以我将其定义为伪分布式,之前写过一篇文章讨论真分布式的原理,可阅读分布...

weixin_30230009的博客 571

三. zemax光学设计流程+案例

文章目录zemax 基本操作:1. 建立光学基本模型2. 像质评价3. 优化(重点)4. 公差分析简单镜头设计 zemax 基本操作: 1. 建立光学基本模型 2. 像质评价 3. 优化(重点) 对系统设定的约束条件或目标值 统称为操作数。 操作数: 光学特性参数(焦距EFFL,近轴放大率PMAG,入瞳位置EMPP) 像差参数(球差SPHA,彗差COMA,像散ASTI) 边界条件(中心厚度值CTVA,边缘厚度值ETAV) 优化的主要步骤: 设置评价函数和优化操作数 一般小像差系统采用波像差 构建

Taiiiii的博客 8050

基于树莓派4B设计的厨房环境监测系统_368

本项目设计并实现了一种基于树莓派4B的厨房环境监测系统,提升厨房使用过程中的安全性与智能化水平。系统采用树莓派4B作为主控核心,集成DHT11温湿度传感器、MQ2烟雾传感器、火焰传感器、光敏传感器、水滴检测传感器等多种传感器,实现对厨房环境温湿度、烟雾、火焰、光照与溢水等参数的实时监测,并通过蜂鸣器进行本地报警。系统通过WIFI连接互联网,利用MQTT协议将环境数据上传至华为云物联网平台,实现远程可视化监控。

415

python-redis-lock应用(入门)

redis-lock

Sudley的博客 948

Qt入门教程【Core篇】singal and slot信号与槽

Qt中信号与槽singal and slot

小鱼酱的博客 2448

分布式锁--PyRedis分布式锁的简单实现

到目前为止,实现的分布式锁基本完备,也没有什么缺陷,同时它的性能也是非常高的。不过由于py-redis的锁实现比较简单,导致拓展性比较低,无法兼容部分场景,同时py-redis库并没有打算开发出包含更多功能的分布锁实现。这意味着开发者只能根据他提供的Lock对象进行重新开发,并通过redis.lock中的lock_class参数传递重新开发后的锁实现。

aobulaien001的博客 309

深入URP之Shader篇1: URP Shader概述

我们知道,URP和HDRP都是基于SRP的,尽管现在可以使用Shader Graph编写Shader,但是手写Shader仍然是有一定的优势,特别是在性能方面,手写Shader可以更极致的优化,因此我们在项目中前期都是直接使用Shader Graph编写Shader,项目后期会改成手写Shader来提高性能。而在SRP中手写Shader,我们仍然使用的是Shader Lab,这个结构并没有太大的变化,但是SRP Shader中使用Shader Lab语言还是有几点不同。

聚焦游戏和图形引擎开发技术 4635

正确解决Authentication plugin ‘caching _sha2_password’ cannot be loaded:异常的有效解决方法

正确解决Authentication plugin ‘caching _sha2_password’ cannot be loaded:异常的有效解决方法

wbajsjhhhhh的博客 1万+

使用 Redis 实现分布式锁

背景 在一般的分布式应用中,要安全有效地同步多服务器多进程之间...

weixin_33691817的博客 125

深度解析:企业AI效能度量框架的核心指标与落地路径,收藏级干货

资源成本:GPU利用率从31%提升到75%,云费用减少了50万元/年;工程效率:模型迭代周期从2周缩短到3天,部署成功率从70%提升到95%;模型性能:CTR从8%提升到12%,CVR从3%提升到4.5%;业务价值:增量收入达200万元/月,ROI达180%;伦理风险:推荐结果的差异影响比从0.7提升到0.9,用户投诉率下降了30%。现在,当CEO再问「AI项目值多少钱?

AI天才研究院 1807

面试不懂分布式锁?那得多吃亏

来源 | 石杉的架构笔记作者 | jianfeng责编 | linse01为什么用分布式锁?在讨论这个问题之前,我们先来看一个业务场景:系统A是一个电商系统,目前是一台机...

形势所逼,不进则退 7401

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

还有人不懂分布式锁的实现就把这篇文章丢给他

前言 大多数互联网系统都是分布式部署的,分布式部署确实能带来性能和效率上的提升,但为此,我们就需要多解决一个分布式环境下,数据一致性的问题。 当某个资源在多系统之间,具有共享性的时候,为了保证大家访问这个资源数据是一致的,那么就必须要求在同一时刻只能被一个客户端处理,不能并发的执行,否者就会出现同一时刻有人写有人读,大家访问到的数据就不一致了。 一、我们为什么需要分布式锁? 在单机时代,虽...

hello 212

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

通俗讲解分布式锁,看完不懂算我输

首先,分布式锁和我们平常讲到的锁原理基本一样,目的就是确保在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法、变量。 在一个进程中,也就是一个 jvm 或者说应用中,我们很容易去处理控制,在 jdk java.util 并发包中已经为我们提供了这些方法去加锁,比如 synchronized 关键字或者 Lock 锁,都可以处理。 但是我们现在的应用程序如果只部署一台服务器,那并发量是很差的,如果同时有上万的请求,很有可能造成服务器压力过大而瘫痪。想想双十一和大年三十晚上十点,瓜分支付宝红包等业

deqing271的博客 416

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

还不懂分布式锁?看看 Redisson 是如何实现分布式锁的!

Java面试笔试面经、Java技术每天学习一点Java面试关注不迷路作者:雨点的名字来源:https://www.cnblogs.com/qdhxhz/Redisson 实现分布式锁有关...

java面试笔试 203

Redis分布式锁,看完不懂你打我

简易的redis分布式锁 加锁: set key my_random_value NX PX 30000 这个命令比setnx好,因为可以同时设置过期时间。不设置过期时间,应用挂了,解不了锁,就一直锁住了。 解锁: if redis.call("get",KEYS[1])==ARGV[1] then return redis.call("del",KEYS[1]) else return 0 end 先比较一下值,相等才删除。防止其他线程把锁给解了。 以上方案在一般的场景..

被基金支配的打工人 281

不懂分布式锁的这些问题,就亏大了

墨墨导读:通过一个很常见的业务场景,引出一个分布式锁的具体方案,如何使用分布式锁呢?通过本文了解下。为什么用分布式锁?在讨论这个问题之前,我们先来看一个业务场景:系统A是...

Enmotech的博客 478

面试不懂分布式锁?那得多吃亏。。。

公众号后台回复“学习”,获取作者独家秘制精品资料多年好友心血力作,阿里资深技术专家十余年JVM生产实践经验《从零开始带你成为JVM实战高手》限时优惠:88元(正在进行in...

石杉的架构笔记 2965

redisson分布式锁_还不懂分布式锁?看看 Redisson 是如何实现分布式锁的

点击上方"码之初"关注,···选择"设为星标"与精品技术文章不期而遇来源:cnblogs.com/qdhxhz/p/11046905.html有关Redisson作为实现分布式锁,总的分3大模块来讲。 1、Redisson实现分布式锁原理 2、Redisson实现分布式锁的源码解析 3、Redisson实现分布式锁的项目代码(可以用于实际项目中)本文只介绍Redisso...

weixin_33592736的博客 467

Redis作者“不懂”分布式锁?

好了,总结一下。这篇文章,我们主要探讨了基于 Redis 实现的分布式锁,究竟是否安全这个问题。从最简单分布式锁的实现,到处理各种异常场景,再到引出 Redlock,以及两个分布式专家的辩论,得出了 Redlock 的适用场景。最后,我们还对比了 Zookeeper 在做分布式锁时,可能会遇到的问题,以及与 Redis 的差异。这里我把这些内容总结成了思维导图,方便你理解。

程序员高级码农的博客 975
上一篇: 想知道北京的公交线路图吗?用python给你画出来
下一篇: 这个库居然能让python提速100倍
python程序员小'鹏
博客等级 码龄6年 163粉丝 87原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值