首页 > 数据库 >Redis锁如何续期?Redis锁超时,任务没完怎么办?

Redis锁如何续期?Redis锁超时,任务没完怎么办?

时间:2024-09-23 13:51:09浏览次数:7  
标签:lock redis client key 续期 超时 id Redis


解决方案

在使用Redis作为分布式锁的存储时,如果一个任务需要长时间执行,并且在这段时间内锁会过期,那么就需要一种机制来自动延长锁的有效期,即续期。通常情况下,可以通过以下几种方式来实现Redis锁的续期:

使用Lua脚本实现续期

一种常见的做法是使用Lua脚本来实现锁的续期。Redis支持在服务端执行Lua脚本,这可以用来实现原子性的操作。当一个任务开始执行时,它会在Redis中设置一个键(例如lock:resource_name),并设置一个初始的过期时间(TTL)。然后,该任务可以定期通过Lua脚本尝试更新这个键的过期时间。如果该键仍然存在并且没有被其他节点持有,则可以成功续期。

自动续期与看门狗机制

另一种方法是创建一个“看门狗”线程或定时器,它负责监控锁的有效期,并在锁快到期前自动延长其有效期。这种机制需要小心处理,以避免在锁已经由另一个节点获取的情况下还试图续期。

使用Redlock算法

Redlock算法是一种分布式锁算法,它可以提供更好的一致性和可用性保证。该算法建议每个锁都有一个有效期限,并且客户端应该定期尝试续期这个锁。如果续期失败了(比如因为网络分区),客户端应该检查是否还持有该锁,如果没有,则不应该继续执行敏感操作。

使用Redisson客户端

如果你使用的是Java,并且想要简化分布式锁的管理,可以考虑使用Redisson客户端,它提供了一个高级API来管理锁。Redisson的RLock可以自动续期,直到你显式地调用unlock方法或者应用程序关闭。

注意事项

  • 重入性:确保锁是可重入的,即相同的持有者可以多次获得同一个锁。
  • 公平性:确保锁的分配是公平的,即按照请求的顺序分配锁。
  • 资源释放:确保在任务结束或异常发生时释放锁,防止死锁。
  • 最终一致性:确保即使在异常情况下,锁最终会被正确地释放。

使用这些策略可以帮助你在任务尚未完成时有效地管理Redis锁的有效期。不过,在设计这样的系统时,还需要考虑到网络延迟、Redis实例的可用性等因素。

代码示例

由于不同的编程语言有不同的实现细节,这里将主要以Python为例进行说明。

方案一:使用Lua脚本实现续期

Lua脚本

首先,我们需要编写一个Lua脚本来实现锁的续期。这个脚本需要做两件事情:

  1. 检查锁是否仍然属于当前持有者。
  2. 如果是,就延长锁的有效期;如果不是,就不做任何操作。
local lockKey = KEYS[1]
local clientId = ARGV[1]
local newTimeout = tonumber(ARGV[2])

-- Check if the lock is held by the client
if redis.call("get", lockKey) == clientId then
    -- Extend the lock timeout
    redis.call("expire", lockKey, newTimeout)
end
Python代码

接下来是在Python中如何使用上述Lua脚本:

import redis
import time
from threading import Thread

def acquire_lock(redis_client, lock_key, client_id, timeout):
    return redis_client.set(lock_key, client_id, nx=True, ex=timeout)

def extend_lock(redis_client, lock_key, client_id, new_timeout):
    lua_script = """
    local lockKey = KEYS[1]
    local clientId = ARGV[1]
    local newTimeout = tonumber(ARGV[2])

    if redis.call("get", lockKey) == clientId then
        redis.call("expire", lockKey, newTimeout)
    end
    """
    # 使用 EVAL 执行 Lua 脚本
    return redis_client.eval(lua_script, 1, lock_key, client_id, new_timeout)

def renew_lock(redis_client, lock_key, client_id, initial_timeout, renew_interval):
    while True:
        # 尝试续期锁
        extend_lock(redis_client, lock_key, client_id, initial_timeout)
        time.sleep(renew_interval)

def main():
    redis_client = redis.Redis(host='localhost', port=6379, db=0)
    lock_key = "lock:example"
    client_id = "client1"
    initial_timeout = 60  # 初始锁超时时间
    renew_interval = 15   # 续期间隔

    # 获取锁
    if acquire_lock(redis_client, lock_key, client_id, initial_timeout):
        print(f"Client {client_id} acquired the lock.")
        
        # 启动续期线程
        renew_thread = Thread(target=renew_lock, args=(redis_client, lock_key, client_id, initial_timeout, renew_interval))
        renew_thread.start()

        # 执行长时间运行的任务
        try:
            do_long_running_task()
        finally:
            # 在任务完成后释放锁
            release_lock(redis_client, lock_key, client_id)
            renew_thread.join()  # 等待续期线程结束
    else:
        print(f"Client {client_id} failed to acquire the lock.")

def release_lock(redis_client, lock_key, client_id):
    if redis_client.get(lock_key) == client_id:
        redis_client.delete(lock_key)

def do_long_running_task():
    # 模拟长时间运行的任务
    time.sleep(120)
    print("Long running task completed.")

if __name__ == '__main__':
    main()

方案二:使用Redlock算法

Redlock算法涉及多个Redis实例来减少单点故障的影响。这里我们不会详细讨论其实现,因为涉及到更复杂的网络和同步问题。

方案三:使用Redisson客户端

Redisson是一个Java客户端,提供了高级功能如自动续期锁等。由于这是一个Java库,这里不提供Python示例。如果你使用Java,可以直接使用Redisson提供的RLock类来简化锁的管理。


标签:lock,redis,client,key,续期,超时,id,Redis
From: https://blog.51cto.com/zhangxueliang/12088984

相关文章

  • Redis简单介绍与安装应用
            在当今的互联网时代,数据的快速存取和处理变得至关重要。Redis,作为一种高性能的键值存储系统,已经成为许多开发者和企业的首选。本文将简要介绍Redis的基本概念、工作原理以及其在实际应用中的一些典型用例。一、简介1)概念        Redis(RemoteDictio......
  • acme+cloudflare生成免费证书(自动续期)
    acmeDNSapiacmeDNSapi的作用是在申请证书时使用dns交易,acme可以通过dnsapi在对应的dns管理平台提交对应的dns记录。玩过证书的朋友都知道,证书申请时有三种验证方式邮箱验证:需要邮箱与域名绑定(细节要求我没试过)文件验证:文件验证时证书管理方会要求你在服务器的指定路径上方一......
  • 国产化:TongRDS替代Redis
    背景:国产化要求,内存数据缓存中间件要换国产产品,这里简单记录一下替换过程,项目是springboot微服务结构。官方文档比较全,这里只是个人记录的最简化的版本。1安装企业版TongRDS分为2个节点,我拿到的版本就是企业版,所以下面的都默认是企业版。分为中心节点和服务节点。安装......
  • SpringBoot中基于JWT的双token(access_token+refresh_token)授权和续期方案
    微服务架构中,JWT认证方案中,用户登录成功后,后端会生成一个JWT格式的access_token并发送给前端。前端接收后,会将此access_token安全地存储在浏览器的LocalStorage中,以便在后续请求中作为身份认证的依据。每次API请求时,前端都会将access_token附加在请求头中发送给后端,后端则通过过......
  • SpringBoot实战:JWT Token 自动续期的解决方案
    前言在前后端分离的开发架构中,当用户成功登录后,后端服务会生成一个JWT(JSONWebTokens)token,并将其返回给前端。前端(如Vue应用)接收到此token后,通常会将其存储在LocalStorage中以方便后续请求时使用。每次向后端发送请求时,前端会将这个token作为请求头的一部分发送给后端,以便后端通......
  • Redis Sentinel:秒杀系统背后的可靠性保障神器!
    哈喽,大家好呀!我是小米,今天我想和大家聊聊如何在个人项目中保证系统的可靠性,尤其是用Redis哨兵模式来保障高可用性。相信很多小伙伴在开发中遇到过Redis挂掉的情况,特别是在高并发场景下,一旦主服务器下线,整个系统可能会因此瘫痪。那我们该如何应对这个问题呢?今天就带大家深入了解......
  • 【解决方案】Java 互联网项目中常见的 Redis 缓存应用场景
    目录前言一、常见key-value二、时效性强三、计数器相关四、高实时性五、排行榜系列六、文章小结前言在笔者3年的Java一线开发经历中,尤其是一些移动端、用户量大的互联网项目,经常会使用到Redis作为缓存中间件的基本工具来解决一些特定的问题。下面是笔者总结梳理的一些常......
  • Redis 内存突增时,如何定量分析其内存使用情况
    背景最近碰到一个case,一个Redis实例的内存突增,used_memory最大时达到了78.9G,而该实例的maxmemory配置却只有16G,最终导致实例中的数据被大量驱逐。以下是问题发生时INFOMEMORY的部分输出内容。# Memoryused_memory:84716542624used_memory_human:78.90Gused_memory_rss:1......
  • Lua 脚本在 Redis 中能够保证操作不会被其他指令插入或打扰 ??
    Lua脚本在Redis中能够保证操作不会被其他指令插入或打扰,主要通过以下机制实现:1.Redis单线程模型2. 脚本执行锁定当执行Lua脚本时,Redis会自动锁定所有在脚本中访问的键。这个过程可以分为几个步骤:获取锁:在脚本执行前,Redis会检查脚本中访问的键。如果有键已经被其他......
  • Redis从基础到实战总结+Redisson分布式锁小结
    一、NoSQL和RDBMS的区别传统的rdbms结构化组织SQL数据和关系都存储在单独的表中操作语言是数据库定义语言严格的一致性基础的事务NoSql不仅仅是数据没有固定的语言键值对存储,列存储、文档存储、图形数据库最终一致性cpa定理和base高性能,高可用,高可扩二、NoSql的四大分类......