Redis SETNX的实现示例

 更新时间:2024年12月27日 11:04:28   作者:飞滕人生TYF  
SETNX是Redis提供的原子操作,用于在指定键不存在时设置键值,并返回操作结果,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

SETNX 是 Redis 提供的一种原子操作,全称是 “SET if Not eXists”,用于在指定的键不存在时设置键值,并返回操作结果。它是实现分布式锁幂等性控制的核心工具之一。

1. SETNX 的基本功能

语法

SETNX key value
  • key:需要设置的键。
  • value:需要设置的值。

返回值

  • 1:如果键不存在,设置成功。
  • 0:如果键已经存在,不执行任何操作。

使用示例

SETNX lock_key "123"

执行结果

  • 如果 lock_key 不存在,则设置键值为 "123",并返回 1
  • 如果 lock_key 已存在,则不执行任何操作,返回 0

2. SETNX 的特性

  • 原子性

    • SETNX 是 Redis 的原子操作,多个客户端并发访问时,只会有一个操作成功。
  • 幂等性

    • 如果键已存在,则后续的 SETNX 调用不会影响当前值。
  • 轻量级锁

    • SETNX 常用于实现分布式锁,通过确保某个键唯一存在来锁定资源。

3. 结合 EXPIRE 的分布式锁

SETNX 本身不能设置过期时间,因此为了避免死锁问题(如客户端异常未释放锁),可以结合 EXPIRE 设置锁的自动过期时间。

问题

  • 如果一个客户端使用 SETNX 获取锁,却因异常无法释放锁,其他客户端可能会永远无法获取锁。

解决方案 1:SETNX + EXPIRE

  • 使用 SETNX 设置锁。
  • 如果锁设置成功,立即设置过期时间。
if redis.call("SETNX", KEYS[1], ARGV[1]) == 1 then
    redis.call("EXPIRE", KEYS[1], ARGV[2])
    return 1
else
    return 0
end

缺点

  • SETNX 和 EXPIRE 是两个独立操作,在高并发情况下可能出现非原子性问题。

解决方案 2:SETNX 改用 SET(推荐)

Redis 提供了改进版本的 SET 命令,可以直接设置键值并附加过期时间:

SET key value NX EX seconds
  • NX:表示仅当键不存在时才执行设置操作(相当于 SETNX)。
  • EX seconds:设置过期时间,单位为秒。

示例

SET lock_key "123" NX EX 10
  • 如果 lock_key 不存在,设置值为 "123",且键将在 10 秒后过期。

优点

  • 原子操作,无需再单独调用 EXPIRE

4. 使用 SETNX 实现分布式锁

SETNX 的一个典型应用是分布式锁,保证在分布式系统中对共享资源的互斥访问。

4.1 基本实现

  • 获取锁

    • 使用 SETNX 尝试设置一个键。
    • 设置成功,表示成功获取锁。
  • 释放锁

    • 检查当前锁是否属于自己(通过唯一标识区分),如果是,则删除锁。

实现逻辑

String lockKey = "lock_key";
String requestId = UUID.randomUUID().toString();
int expireTime = 10;

// 获取锁
if (redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, TimeUnit.SECONDS)) {
    try {
        // 处理业务逻辑
    } finally {
        // 释放锁
        if (requestId.equals(redisTemplate.opsForValue().get(lockKey))) {
            redisTemplate.delete(lockKey);
        }
    }
} else {
    // 获取锁失败
    System.out.println("Lock is already held by another process.");
}

4.2 Lua 脚本保证原子性

为了确保释放锁的操作是原子的,可以使用 Lua 脚本完成判断和删除:

if redis.call("get", KEYS[1]) == ARGV[1] then
    return redis.call("del", KEYS[1])
else
    return 0
end

调用示例

String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                "return redis.call('del', KEYS[1]) " +
                "else return 0 end";
redisTemplate.execute(new DefaultRedisScript<>(script, Long.class), 
                      Collections.singletonList(lockKey), requestId);

5. SETNX 的典型应用场景

5.1 分布式锁

  • 确保资源互斥访问,防止并发修改造成数据错误。

5.2 请求去重

  • 对同一用户的重复请求设置唯一标识,防止重复处理。
  • 示例:使用 SETNX 设置请求 ID,只有第一次请求会被处理。

5.3 幂等性控制

  • 确保某些操作(如支付、扣款)不会因重复请求而执行多次。

6. SETNX 的优缺点

优点缺点
原子性强,适合高并发场景无法直接设置过期时间
实现简单,易于集成到业务逻辑中需要结合 EXPIRE 或改用 SET 命令
性能高,Redis 本身支持高吞吐量需要额外处理死锁或锁释放的边界条件

7. SETNX 的改进建议

  • 尽量使用 SET key value NX EX seconds 替代 SETNX

    • 提供了原生的过期时间设置,简化了分布式锁的实现。
  • 结合 Lua 脚本

    • 使用 Lua 脚本处理复杂逻辑,保证操作的原子性。
  • 监控锁的状态

    • 对于长期持有锁的操作,应增加心跳机制,防止锁意外释放。
  • 锁争抢优化

    • 避免高并发环境下大量线程重复尝试获取锁,可以结合延时队列或限流机制。

8. 总结

  • SETNX 是 Redis 中一种简单、高效的原子操作,主要用于确保键不存在时的设置操作。
  • 它是实现分布式锁的基础,但需要与 EXPIRE 或其他命令结合使用,避免死锁问题。
  • 在现代应用中,建议优先使用 Redis 的 SET NX EX 命令,进一步提升功能的原子性和易用性。
  • 合理利用 SETNX,可以在分布式场景中有效解决资源争抢、重复请求和幂等性问题。

到此这篇关于Redis SETNX的实现示例的文章就介绍到这了,更多相关Redis SETNX内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 安装redis(windows和Ubuntu)详解

    安装redis(windows和Ubuntu)详解

    这篇文章主要介绍了Redis在Ubuntu和Windows下的安装,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-04-04
  • redis缓存的简单操作(get、put)

    redis缓存的简单操作(get、put)

    这篇文章主要介绍了redis缓存的简单操作,包括引入jedisjar包、配置redis、RedisDao需要的一些工具等,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-09-09
  • 如何在SpringBoot中使用Redis实现分布式锁

    如何在SpringBoot中使用Redis实现分布式锁

    这篇文章主要介绍了如何在SpringBoot中使用Redis实现分布式锁,在实际开发中有可能会遇到多个线程同时访问同一个共享变量,那么上锁就很重要了,需要的朋友可以参考下
    2023-03-03
  • Redis使用命令行与多数据库配置

    Redis使用命令行与多数据库配置

    本文详细讲解了Redis使用命令行与多数据库配置的方法,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-03-03
  • Redis的持久化方式

    Redis的持久化方式

    Redis提供了两种主要的持久化方式:RDB和AOF,RDB通过定时快照的方式保存数据状态,而AOF记录每个写操作以便于重启时重放,两者可以结合使用,且在重启时AOF文件会被优先用于数据恢复,RDB快照具有速度快、节省磁盘空间的优点,但可能会丢失最近的数据
    2024-10-10
  • 了解Redis常见应用场景

    了解Redis常见应用场景

    Redis是一个key-value存储系统,现在在各种系统中的使用越来越多,大部分情况下是因为其高性能的特性,被当做缓存使用,这里介绍下Redis经常遇到的使用场景
    2021-06-06
  • Linux服务器使用Redis作为数据缓存并用log4j2进行日志记录的过程分享

    Linux服务器使用Redis作为数据缓存并用log4j2进行日志记录的过程分享

    这篇文章主要介绍了Linux服务器使用Redis作为数据缓存并用log4j2日志记录,关于SpringBoot项目配置Redis与log4j2是查询官方文档,本文中的Redis配置类、Redis工具类以及log4j2.xml配置文件来自网络,查证源自何处比较麻烦,所以在此感谢所有人的分享
    2023-09-09
  • 关于redigo中PubSub的一点小坑分析

    关于redigo中PubSub的一点小坑分析

    这篇文章主要给大家介绍了关于redigo中PubSub的一点小坑的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-01-01
  • 基于Redis实现API接口访问次数限制

    基于Redis实现API接口访问次数限制

    日常开发中会有一个常见的需求,需要限制接口在单位时间内的访问次数,比如说某个免费的接口限制单个IP一分钟内只能访问5次,该怎么实现呢,本文小编给大家介绍了如何基于Redis实现API接口访问次数限制,需要的朋友可以参考下
    2024-11-11
  • Redis分布式锁一定要避开的两个坑

    Redis分布式锁一定要避开的两个坑

    这篇文章主要为大家详细介绍了Redis中分布式锁一定要避开的两个坑以及对应的解决方法,文中的示例代码讲解详细,希望对大家有所帮助
    2023-04-04

最新评论