如何使用Python aioredis实现异步分布式锁功能?

大磊酱_2672

大磊酱_2672

2026-06-23

828人浏览

原创

aioredis==1.x的lock()不可靠,因其加锁后释放时直接DEL且不校验持有者身份,缺乏Lua脚本保障的“验证+删除”原子性,易致误删他人锁;而redis.asyncio(aioredis 2.x)已修复此问题并支持自动续期与token校验。

如何使用python aioredis实现异步分布式锁功能?

为什么直接用 aioredis.Redis.lock() 不可靠

因为 aioredis(特指 aioredis==1.x)的 lock() 方法底层调用的是 Redis 的 SET key value EX seconds NX,但它**不校验锁持有者身份**,且释放时直接 DEL,存在误删风险。更关键的是:它没有实现原子性的「校验+删除」(即 Lua 脚本 unlock),在多实例竞争下极易出现锁被错误释放的问题。

如果你用的是 aioredis>=2.0(已重命名为 redis,模块为 redis.asyncio),它的 Lock 类已修复该问题,但接口和行为与 1.x 不兼容——所以必须先确认你实际用的版本:

  • pip show aioredis → 若输出 Version: 1.3.1,则仍在用旧版,不能直接信任其 lock()
  • import aioredis; print(aioredis.__version__) → 同上

手写一个带 owner 校验的异步分布式锁

核心是两个原子操作:加锁用 SET key value EX seconds NX,解锁用 Lua 脚本比对 value(即唯一 owner token)再删。token 必须全局唯一(推荐用 uuid4().hex),避免不同协程误删。

下面是一个最小可用实现(适配 aioredis==1.3.1):

import aioredis
import uuid
import asyncio
<p>class AsyncRedisLock:
def <strong>init</strong>(self, redis, key, timeout=30):
self.redis = redis
self.key = key
self.timeout = timeout
self.token = None  # 懒生成,避免无谓 uuid 调用</p><pre class="brush:php;toolbar:false;">async def acquire(self, blocking=True, blocking_timeout=None):
    if self.token is not None:
        return False  # 已持有锁,不可重入
    token = uuid.uuid4().hex
    # 使用 SET 命令的 NX+EX+VALUE 原子性保证
    ok = await self.redis.execute('SET', self.key, token, 'EX', self.timeout, 'NX')
    if ok == b'OK':
        self.token = token
        return True
    if not blocking:
        return False
    # 阻塞等待:轮询 + sleep,生产环境建议加 jitter 和最大重试
    while blocking:
        await asyncio.sleep(0.1)
        ok = await self.redis.execute('SET', self.key, token, 'EX', self.timeout, 'NX')
        if ok == b'OK':
            self.token = token
            return True
    return False

async def release(self):
    if not self.token:
        raise ValueError("Cannot release unacquired lock")
    # Lua 脚本确保只有 own token 才能删
    script = """
    if redis.call("GET", KEYS[1]) == ARGV[1] then
        return redis.call("DEL", KEYS[1])
    else
        return 0
    end
    """
    ret = await self.redis.eval(script, keys=[self.key], args=[self.token])
    if ret != 1:
        raise RuntimeError(f"Failed to release lock {self.key}: token mismatch or key missing")
    self.token = None

注意点:

Biopython
Biopython

全面的分子生物学工具集。适用于序列操作、文件解析(FASTA/GenBank/PDB)、系统发育分析,以及通过 Bio.Entrez 以编程方式访问 NCBI/

下载
  • blocking_timeout 参数这里没实现,如需硬超时,得在外层加 asyncio.wait_for()
  • 轮询间隔 0.1s 是平衡延迟与 Redis 压力的经验值,高并发场景可动态调整
  • 务必用 self.token 记录本次获取的 token,不能每次 release 都新生成

用 redis.asyncio(新版)更省心但要改依赖

如果你能升级到 redis>=4.2.0(即原 aioredis 2.x 的继任者),直接用内置 Lock 即可,它默认使用 Lua 解锁、支持自动续期(extend)、且线程/协程安全:

import redis.asyncio as redis
<p>r = redis.Redis(host='localhost', port=6379, db=0)
lock = r.lock("my_resource", timeout=60, blocking_timeout=5)</p><p>async def do_work():
if await lock.acquire():
try:</p><h1>执行临界区逻辑</h1><pre class="brush:php;toolbar:false;">        await asyncio.sleep(1)
    finally:
        await lock.release()  # 自动校验 owner

关键差异:

  • timeout 是锁自动过期时间(秒),不是阻塞等待时间
  • blocking_timeout 是 acquire 时最多等多久,超时返回 False
  • 内部 token 是随机字符串,unlock 通过 Lua 严格校验,无需手动管理
  • 若任务执行时间可能超过 timeout,可调用 await lock.extend(30) 续期

最容易被忽略的三个坑

分布式锁不是加了 SET NX 就万事大吉:

  • Redis 实例单点故障?锁失效 → 生产必须用 Redis Sentinel 或 Cluster,并确认 client 支持 failover 后自动重连
  • 锁超时时间设太短,业务没做完锁就丢了 → 要根据 P99 业务耗时 + 安全余量设定,别拍脑袋写 5 秒
  • 忘记异常路径下的 release → 务必用 try/finally 或异步上下文管理器(async with lock:),新版 redis.asyncio.Lock 支持后者,旧版需自行封装

真正难的从来不是写几行 Lua,而是把超时、续期、故障转移、监控告警串成闭环。

Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!

相关文章

PHP速学视频免费教程(入门到精通)
PHP速学视频免费教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

python redis

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
python打包成可执行文件
python打包成可执行文件

本专题为大家带来python打包成可执行文件相关的文章,大家可以免费的下载体验。

2023.07.20

1931

4

python能做什么
python能做什么

python能做的有:可用于开发基于控制台的应用程序、多媒体部分开发、用于开发基于Web的应用程序、使用python处理数据、系统编程等等。本专题为大家提供python相关的各种文章、以及下载和课程。

2023.07.25

5464

7

format在python中的用法
format在python中的用法

Python中的format是一种字符串格式化方法,用于将变量或值插入到字符串中的占位符位置。通过format方法,我们可以动态地构建字符串,使其包含不同值。php中文网给大家带来了相关的教程以及文章,欢迎大家前来阅读学习。

2023.07.31

1889

3

python教程
python教程

Python已成为一门网红语言,即使是在非编程开发者当中,也掀起了一股学习的热潮。本专题为大家带来python教程的相关文章,大家可以免费体验学习。

2023.08.03

33237

23

python环境变量的配置
python环境变量的配置

Python是一种流行的编程语言,被广泛用于软件开发、数据分析和科学计算等领域。在安装Python之后,我们需要配置环境变量,以便在任何位置都能够访问Python的可执行文件。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.04

3907

5

python eval
python eval

eval函数是Python中一个非常强大的函数,它可以将字符串作为Python代码进行执行,实现动态编程的效果。然而,由于其潜在的安全风险和性能问题,需要谨慎使用。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.04

3907

5

scratch和python区别
scratch和python区别

scratch和python的区别:1、scratch是一种专为初学者设计的图形化编程语言,python是一种文本编程语言;2、scratch使用的是基于积木的编程语法,python采用更加传统的文本编程语法等等。本专题为大家提供scratch和python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

1283

5

python合并两个列表
python合并两个列表

Python是一种强大的编程语言,具有许多方便的功能和工具。在Python中,有多种方法可以合并两个列表。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

2023.08.10

616

4

python是前端还是后端
python是前端还是后端

Python属于前端也属于后端,其灵活性和丰富的生态系统使得开发人员能够在不同的领域中灵活运用。本专题为大家提供python相关的文章、下载、课程内容,供大家免费下载体验。

2023.08.11

3043

5

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程