当前位置: 首页 > news >正文

简单认识redis - 12 redis锁

体样式**redis中,不同的问题有不一样的解决办法,那么锁也有不同的锁来解决不一样的问题,下面将举出几个常用的redis锁。

1. SETNX锁(简单独占锁)

  • 原理
    • SETNX(SET if Not eXists)是Redis实现简单锁的命令。它的操作是原子性的,当尝试设置一个键值对时,如果键不存在,则设置成功并返回1,表示获取锁成功;如果键已经存在,则返回0,表示锁已被其他客户端占用。
  • 示例
    • 假设使用lock_key作为锁的键,locked_value作为锁的值,在Java中可以这样实现:
      import redis.clients.jedis.Jedis;public class SimpleRedisLock {public static void main(String[] args) {Jedis jedis = new Jedis("localhost", 6379);String lock_key = "my_lock";String locked_value = "locked";long result = jedis.setnx(lock_key, locked_value);if (result == 1) {System.out.println("获取锁成功");// 执行业务逻辑try {// 模拟业务逻辑执行时间Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}// 释放锁,这里简单地删除键jedis.del(lock_key);} else {System.out.println("获取锁失败");}jedis.close();}
      }
      
  • 局限性
    • 没有自动释放机制。如果获取锁的客户端崩溃或者出现异常没有执行释放锁的操作,这个锁将一直被占用,导致死锁。

2. 带有过期时间的SET锁

  • 原理
    • Redis的SET命令可以在设置键值对的同时设置过期时间,并且这个操作是原子性的。例如,SET key value EX seconds NX这样的命令格式,其中EX seconds表示设置过期时间为seconds秒,NX表示只有当键不存在时才设置。
  • 示例
    • 在Python中,可以这样实现:
      import redis
      import timer = redis.Redis(host='localhost', port=6379)
      lock_key = "my_lock"
      locked_value = "locked"
      expire_time = 10
      result = r.set(lock_key, locked_value, ex=expire_time, nx=True)
      if result:print("获取锁成功")try:# 执行业务逻辑time.sleep(7)finally:# 不需要手动删除锁,过期后自动释放pass
      else:print("获取锁失败")
      
  • 注意事项
    • 业务逻辑执行时间如果超过了锁的过期时间,可能会出现数据不一致的情况。例如,在锁过期后,其他客户端获取了锁,然后原来的客户端完成业务逻辑并释放锁,这可能会导致新获取锁的客户端的操作受到干扰。

3. RedLock(分布式锁算法)

  • 原理
    • RedLock算法用于在多个Redis节点组成的分布式环境中获取高可靠的锁。它要求在多个(通常是奇数个)独立的Redis节点上尝试获取锁,只有当大多数节点(例如,总共5个节点,至少3个节点)成功获取锁时,才认为锁获取成功。
  • 示例流程(简化)
    • 假设有3个Redis节点node1node2node3,在Python中实现RedLock的基本步骤如下:
      import redis
      import time# 定义Redis节点列表
      redis_nodes = [redis.Redis(host='node1', port=6379),redis.Redis(host='node2', port=6379),redis.Redis(host='node3', port=6379)
      ]lock_key = "my_redlock"
      locked_value = "unique_client_id"
      expire_time = 10
      quorum = len(redis_nodes) // 2 + 1
      acquired_locks = 0# 尝试在多个节点上获取锁
      for node in redis_nodes:try:result = node.set(lock_key, locked_value, ex=expire_time, nx=True)if result:acquired_locks += 1except:pass
      if acquired_locks >= quorum:print("RedLock获取成功")try:# 执行业务逻辑time.sleep(7)finally:# 释放锁,需要向所有节点发送释放请求for node in redis_nodes:try:node.delete(lock_key)except:pass
      else:print("RedLock获取失败")
      
  • 优势与应用场景
    • 提供了更高的可靠性,能够抵抗单个Redis节点故障、网络分区等问题,适用于对数据一致性要求极高的分布式系统,如金融交易系统、分布式任务调度系统等。

4. 基于Lua脚本实现的复杂锁(如可重入锁)

  • 原理
    • Lua脚本在Redis中可以原子地执行多个命令,通过编写Lua脚本可以实现复杂的锁逻辑。例如,实现可重入锁时,可以在脚本中检查锁的持有者是否为当前客户端,如果是,则增加重入次数;如果不是,则判断是否可以获取锁。
  • 示例(简单的可重入锁检查脚本)
    • 以下是一个简单的Redis Lua脚本用于检查可重入锁(假设锁的信息存储在一个哈希表中,键为lock_key,字段owner存储持有者,count存储重入次数):
      -- 检查锁是否存在且持有者是当前客户端
      local lock_key = KEYS[1]
      local client_id = ARGV[1]
      local lock_info = redis.call('hgetall', lock_key)
      if #lock_info == 0 then-- 锁不存在,设置持有者为当前客户端,重入次数为1redis.call('hmset', lock_key, 'owner', client_id, 'count', 1)return 1
      elseif lock_info[2] == client_id then-- 持有者是当前客户端,增加重入次数redis.call('hincrby', lock_key, 'count', 1)return 1
      elsereturn 0
      end
      
    • 在Java中调用这个Lua脚本可以这样实现:
      import redis.clients.jedis.Jedis;
      import redis.clients.jedis.JedisPool;
      import redis.clients.jedis.JedisPoolConfig;
      import redis.clients.jedis.Response;
      import redis.clients.jedis.ScriptingCommands;public class ReentrantRedisLock {private static final String LUA_SCRIPT = "local lock_key = KEYS[1]\n" +"local client_id = ARGV[1]\n" +"local lock_info = redis.call('hgetall', lock_key)\n" +"if #lock_info == 0 then\n" +"    -- 锁不存在,设置持有者为当前客户端,重入次数为1\n" +"    redis.call('hmset', lock_key, 'owner', client_id, 'count', 1)\n" +"    return 1\n" +"elseif lock_info[2] == client_id then\n" +"    -- 持有者是当前客户端,增加重入次数\n" +"    redis.call('hincrby', lock_key, 'count', 1)\n" +"    return 1\n" +"else\n" +"    return 0\n" +"end";private JedisPool jedisPool;public ReentrantRedisLock() {JedisPoolConfig poolConfig = new JedisPoolConfig();jedisPool = new JedisPool(poolConfig, "localhost", 6379);}public boolean tryLock(String lock_key, String client_id) {try (Jedis jedis = jedisPool.getResource()) {ScriptingCommands scriptingCommands = jedis.scriptingCommands();Response<Long> response = scriptingCommands.evalsha(scriptingCommands.scriptLoad(LUA_SCRIPT), 1, lock_key, client_id);return response.get() == 1;}}public void unlock(String lock_key, String client_id) {try (Jedis jedis = jedisPool.getResource()) {// 这里还需要编写释放锁的Lua脚本,此处省略// 基本思路是检查重入次数,减1后如果为0则删除锁的记录}}
      }
      
  • 优势与应用场景
    • 可以实现更灵活、复杂的锁机制,如可重入锁、公平锁等,满足特定业务场景下对锁的高级需求,如在递归调用函数中需要获取同一把锁,或者需要按照请求顺序公平地获取锁的场景。
http://www.lryc.cn/news/474842.html

相关文章:

  • 基于springboot+vue车辆充电桩管理系统
  • shodan用法(完)
  • 【若依框架】代码生成详细教程,15分钟搭建Springboot+Vue3前后端分离项目,基于Mysql8数据库和Redis5,管理后台前端基于Vue3和Element Plus,开发小程序数据后台
  • 转子侧串级调速系统和双馈调速系统
  • AI学习指南自然语言处理篇-Transformer模型的实践
  • 【LVGL速成】LVGL修改标签文本(GUI Guider生成的字库问题)
  • C语言项目实践-贪吃蛇
  • 在kanzi 3.9.8里使用API创建自定义材质
  • IDEA中通义灵码的使用技巧
  • JS中let var 和const区别
  • ansible详细介绍和具体步骤
  • 利用LangChain与LLM打造个性化私有文档搜索系统
  • linux中的软、硬链接
  • Ubuntu 系统、Docker配置、Docker的常用软件配置(下)
  • jdk,openjdk,oraclejdk
  • Docker Hub 镜像加速器
  • DevOps赋能:优化业务价值流的实战策略与路径(上)
  • int的取值范围
  • 图文检索(16):IDC: Boost Text-to-Image Retrieval via Indirect and Direct Connections
  • 企业数字化转型:重识、深思、重启新征程-亿发
  • 仓颉刷题录-字符串数字转换(一)
  • SpringBoot【实用篇】- 配置高级
  • liunx CentOs7安装MQTT服务器(mosquitto)
  • 【银河麒麟高级服务器操作系统】虚拟机lvm分区丢失现象分析及解决建议
  • Android 原子性类型都有哪些
  • MySQL(上)
  • ffplay 实现视频流中音频的延迟
  • CSDN资源变现
  • Rembg模型构建教程
  • 向量的基础知识和矩阵向量的坐标旋转