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

Redis的常用命令及`SETNX`实现分布式锁、幂等操作

文章目录

    • 一. Redis相关命令详解
      • 1. 相关命令详解
        • (1)SET(基础设置命令)
        • (2)GET(获取值)
        • (3)DEL(删除键)
        • (4)EXPIRE(设置过期时间)
        • (5)TTL(查看剩余生存时间)
        • (6)INCR(原子递增)
      • 1. 命令使用场景
        • (1)分布式锁
        • (2)限流器
        • (3)缓存防穿透
      • 3. 注意事项
      • 4. 总结
    • 二、分布式锁实现
      • 核心步骤
      • 代码示例(使用Jedis)
      • 关键点
    • 三、幂等操作实现
      • 核心逻辑
      • 代码示例:
      • 关键点
    • 四、生产环境注意事项
    • 五、两种场景对比


一. Redis相关命令详解

1. 相关命令详解

(1)SET(基础设置命令)
  • 作用:设置键值对(会覆盖旧值)。
  • 扩展选项(Redis 2.6.12+):
    • EX seconds:设置过期时间(秒)。
    • PX milliseconds:设置过期时间(毫秒)。
    • NX:等价于 SETNX(key 不存在才设置)。
    • XX:key 存在时才设置。
  • 示例
    SET lock:res001 "token" EX 10 NX  # 设置分布式锁(10秒后自动释放)
    
(2)GET(获取值)
  • 命令格式GET key
  • 返回值:key 的值;若 key 不存在,返回 nil
  • 示例
    GET mykey  # 返回 "Hello"
    
(3)DEL(删除键)
  • 命令格式DEL key [key ...]
  • 作用:删除一个或多个 key。
  • 返回值:被删除 key 的数量。
  • 示例
    DEL mykey  # 删除 mykey
    
(4)EXPIRE(设置过期时间)
  • 命令格式EXPIRE key seconds
  • 作用:为 key 设置生存时间(秒)。
  • 返回值
    • 1:设置成功。
    • 0:key 不存在或设置失败。
  • 示例
    EXPIRE mykey 60  # 60秒后 mykey 自动删除
    
(5)TTL(查看剩余生存时间)
  • 命令格式TTL key
  • 返回值
    • 剩余生存时间(秒)。
    • -2:key 不存在。
    • -1:key 存在但未设置过期时间。
  • 示例
    TTL mykey  # 返回剩余秒数
    
(6)INCR(原子递增)
  • 命令格式INCR key
  • 作用:将 key 储存的整数值增加 1。若 key 不存在,先初始化为 0 再执行。
  • 返回值:递增后的值。
  • 示例
    SET counter 10
    INCR counter  # 返回 11
    

1. 命令使用场景

(1)分布式锁
# 加锁(设置锁 + 10秒过期)
SET lock:order123 "uuid" EX 10 NX# 解锁(先检查值再删除)
if GET lock:order123 == "uuid":DEL lock:order123

注意:更推荐使用 Redlock 算法或 Lua 脚本保证原子性。

(2)限流器

利用 INCREXPIRE 实现简单限流:

# 每秒限流 10 次
KEY = "rate_limit:user123"
COUNT = INCR KEY
if COUNT == 1:EXPIRE KEY 1  # 第一次设置过期时间
if COUNT > 10:return "请求过多"
(3)缓存防穿透
# 查询数据库前,用 SETNX 设置空值标记
if SETNX cache:key:empty "":EXPIRE cache:key:empty 300  # 短期缓存空值

3. 注意事项

  1. 原子性
    SETNX + EXPIRE 是非原子操作!推荐使用 SET key value EX time NX
  2. 分布式锁续期
    考虑用 Redisson 或 Lua 脚本实现锁续期(避免业务未完成锁已过期)。
  3. 值覆盖风险
    解锁时需验证 value(如 UUID),防止误删其他客户端的锁。

4. 总结

命令作用典型场景
SETNXkey 不存在时设置值分布式锁、幂等操作
SET ... NX增强版 SETNX(支持过期时间)安全的分布式锁
INCR原子递增计数器限流器、计数器
EXPIRE设置 key 过期时间缓存管理、临时数据

二、分布式锁实现

通过SET key value NX EX time out实现原子性加锁和超时设置。

核心步骤

  1. 加锁:使用唯一值(如UUID)作为value,避免误删其他客户端的锁
  2. 解锁:通过Lua脚本校验value一致性,保证原子性删除
  3. 超时机制:自动释放锁防止死锁

代码示例(使用Jedis)

import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
import java.util.UUID;public class RedisDistributedLock {private static final String LOCK_SUCCESS = "OK";private static final Long RELEASE_SUCCESS = 1L;private static final int DEFAULT_EXPIRE_TIME = 30; // 默认锁超时30秒// 获取锁public static String acquireLock(Jedis jedis, String lockKey, int expireTime) {String requestId = UUID.randomUUID().toString();SetParams params = SetParams.setParams().nx().ex(expireTime);String result = jedis.set(lockKey, requestId, params);return LOCK_SUCCESS.equals(result) ? requestId : null;}// 释放锁(Lua脚本保证原子性)public static boolean releaseLock(Jedis jedis, String lockKey, String requestId) {String luaScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +"return redis.call('del', KEYS[1]) " +"else return 0 end";Object result = jedis.eval(luaScript, 1, lockKey, requestId);return RELEASE_SUCCESS.equals(result);}// 使用示例public static void main(String[] args) {try (Jedis jedis = new Jedis("localhost")) {String lockKey = "order_lock_123";String requestId = acquireLock(jedis, lockKey, DEFAULT_EXPIRE_TIME);if (requestId != null) {try {// 执行业务操作System.out.println("执行业务逻辑...");} finally {releaseLock(jedis, lockKey, requestId);}} else {System.out.println("获取锁失败");}}}
}

关键点

  • 唯一value:使用UUID防止误删其他请求的锁
  • 原子解锁:Lua脚本确保GET + DEL的原子性
  • 超时兜底:即使业务崩溃,锁也会自动释放

三、幂等操作实现

利用SET key unique_token NX EX timeout标记已处理请求。

核心逻辑

  1. 客户端生成唯一请求ID(如订单ID+业务类型)
  2. 执行SET命令尝试写入Redis
  3. 返回OK表示首次请求,执行业务
  4. 返回null表示重复请求,直接跳过

代码示例:

import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;public class IdempotentProcessor {private static final int TOKEN_EXPIRE = 24 * 3600; // 令牌过期时间24小时public static boolean isFirstRequest(Jedis jedis, String requestId) {// 尝试设置键值(存在则失败)SetParams params = SetParams.setParams().nx().ex(TOKEN_EXPIRE);return "OK".equals(jedis.set(requestId, "1", params));}// 使用示例(支付幂等校验)public static void processPayment(String orderId, double amount) {try (Jedis jedis = new Jedis("localhost")) {String requestKey = "pay:" + orderId; // 如: pay:ORDER_20230710_001if (isFirstRequest(jedis, requestKey)) {// 首次请求,执行业务System.out.println("处理支付: " + amount);// TODO: 数据库操作等} else {// 重复请求System.out.println("重复请求,直接返回");}}}
}

关键点

  • 唯一请求标识:使用业务唯一键(如订单ID)
  • 自动过期:设置合理过期时间清理旧数据
  • 原子操作SET NX保证并发安全

四、生产环境注意事项

  1. 锁续期问题
    对于长任务,使用守护线程定时续期(类似Redisson的WatchDog)
  2. 集群环境
    Redis主从切换可能导致锁失效,考虑使用Redlock算法(但有争议)
  3. 幂等键设计
    业务:唯一ID格式(如 order:pay:1001
  4. 网络超时
    添加重试机制(但需避免雪崩)
  5. 替代方案
    复杂场景建议使用成熟库:
    • 分布式锁:Redisson
    • 幂等处理:Spring的@Idempotent注解 + 中间件

五、两种场景对比

特性分布式锁幂等操作
目的互斥访问共享资源防止重复提交/处理
核心命令SET key uuid NX EXSET key requestId NX EX
数据存储要求临时数据(自动过期)需保留至业务周期结束(设置长TTL)
典型应用场景库存扣减、订单创建支付回调、消息去重

通过合理组合这两种机制,可解决大多数分布式并发问题。

http://www.dtcms.com/a/272474.html

相关文章:

  • Redis Stack扩展功能
  • K8S数据流核心底层逻辑剖析
  • AI进化论06:连接主义的复兴——神经网络的“蛰伏”与“萌动”
  • k8s集群--证书延期
  • 项目进度管控依赖Excel,如何提升数字化能力
  • 调度器与闲逛进程详解,(操作系统OS)
  • UI前端与数字孪生结合案例分享:智慧城市的智慧能源管理系统
  • 数据结构笔记10:排序算法
  • Windows 本地 使用mkcert 配置HTTPS 自签名证书
  • Java并发 - 阻塞队列详解
  • XSS(ctfshow)
  • 文心大模型4.5开源测评:保姆级部署教程+多维度测试验证
  • 图书管理系统(完结版)
  • PyCharm 中 Python 解释器的添加选项及作用
  • 创始人IP如何进阶?三次关键突破实现高效转化
  • QT解析文本框数据——详解
  • pycharm中自动补全方法返回变量
  • 自动化脚本配置网络IP、主机名、网段
  • React封装过哪些组件-下拉选择器和弹窗表单
  • 常用的.gitconfig 配置
  • 【显示模块】嵌入式显示与触摸屏技术理论
  • HarmonyOS AI辅助编程工具(CodeGenie)UI生成
  • 时序数据库的存储之道:从数据特性看技术要点
  • 使用深度学习框架yolov8训练监控视角下非机动车电动车头盔佩戴检测数据集VOC+YOLO格式11999张4类别步骤和流程
  • UEditor 对接 秀米 手机编辑器流程与问题
  • ClickHouse 查看正在执行的SQL查询
  • Django--01基本请求与响应流程
  • go go go 出发咯 - go web开发入门系列(四) 数据库ORM框架集成与解读
  • selenium跳转到新页面时如何进行定位
  • 前缀和|差分