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 fork进程分配不到内存解决方案

    Redis fork进程分配不到内存解决方案

    这篇文章主要介绍了Redis fork进程分配不到内存解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-11-11
  • Redis的使用模式之计数器模式实例

    Redis的使用模式之计数器模式实例

    这篇文章主要介绍了Redis的使用模式之计数器模式实例,本文讲解了汇总计数器、按时间汇总的计数器、速度控制、使用 Hash 数据类型维护大量计数器等内容,需要的朋友可以参考下
    2015-03-03
  • redis快照模式_动力节点Java学院整理

    redis快照模式_动力节点Java学院整理

    这篇文章主要为大家详细介绍了redis快照模式的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-08-08
  • Redis中主键失效的原理及实现机制剖析

    Redis中主键失效的原理及实现机制剖析

    这篇文章主要介绍了Redis中主键失效的原理及实现机制剖析,本文讲解了失效时间的控制、失效的内部实现、Memcached 删除失效主键的方法与 Redis 有何异同、Redis 的主键失效机制会不会影响系统性能等内容,需要的朋友可以参考下
    2015-06-06
  • Redisson之lock()和tryLock()的区别及说明

    Redisson之lock()和tryLock()的区别及说明

    这篇文章主要介绍了Redisson之lock()和tryLock()的区别及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • Redis实现订单过期删除的方法步骤

    Redis实现订单过期删除的方法步骤

    本文主要介绍了Redis实现订单过期删除的方法步骤,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • Redis设置密码的实现步骤

    Redis设置密码的实现步骤

    本文主要介绍了Redis设置密码的实现步骤,主要包括两种方法:临时密码和持久密码,具有一定的参考价值,感兴趣的可以了解一下
    2023-08-08
  • Redis数据结构SortedSet的底层原理解析

    Redis数据结构SortedSet的底层原理解析

    这篇文章主要介绍了Redis数据结构SortedSet的底层原理解析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • redis分布式锁与zk分布式锁的对比分析

    redis分布式锁与zk分布式锁的对比分析

    这篇文章主要介绍了redis分布式锁与zk分布式锁的对比分析,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-11-11
  • 详解缓存穿透击穿雪崩解决方案

    详解缓存穿透击穿雪崩解决方案

    在我们日常的开发中,有时需要系统在极短的时间内完成成千上万次的读/写操作,这个时候不是数据库能够承受的,通常会引入NoSQL技术。redis技术就是NoSQL技术中的一种,但是引入redis又有可能出现缓存穿透,缓存击穿,缓存雪崩等问题。本文就对这三种问题进行较深入剖析。
    2021-05-05

最新评论