跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava算法

Java 中使用 Lua 脚本实现 Redis 原子操作与高并发场景优化

综述由AI生成探讨在 Java 开发中如何利用 Redis Lua 脚本解决高并发下的数据一致性问题。通过对比普通多步调用与 Lua 脚本执行,阐述了 Lua 的原子性和减少网络开销的优势。详细介绍了 EVAL 与 SCRIPT LOAD+EVALSHA 的执行差异及生产环境中的降级处理方案。列举了分布式锁、秒杀扣库存、滑动窗口限流三个高频实战场景的 Lua 源码。最后总结了慢脚本、外部依赖及 Key 路由等避坑指南,帮助开发者安全高效地利用 Redis 进行复杂逻辑处理。

SecGuard发布于 2026/3/15更新于 2026/5/2830 浏览
Java 中使用 Lua 脚本实现 Redis 原子操作与高并发场景优化

在这里插入图片描述

你在做一个电商系统的'秒杀扣库存'功能。 你的 Java 代码逻辑:

  1. int stock = redis.get("sku:1001:stock"); (获取库存)
  2. if (stock > 0) { redis.decr("sku:1001:stock"); } (库存大于 0 则扣减)

灾难降临: 这两步操作之间有网络延迟,并且不是原子的。 当库存只剩 1 个时,100 个并发线程同时执行到了第 1 步,都发现 stock = 1 > 0,于是全部执行了第 2 步。 结果: 库存变成了 -99,超卖了 99 件商品,老板连夜找你谈话。 破局之道: 使用 Redis Lua 脚本。将'查库存 + 判断 + 扣减'打包成一段脚本发给 Redis。因为 Redis 执行 Lua 脚本是单线程且排他的,这 100 个请求只能乖乖排队一个个执行,完美解决超卖问题。


1. 核心原理:为什么是 Lua?

Redis 官方内置了 Lua 解析器。它能带来两大无可替代的优势:

  1. 绝对的原子性 (Atomicity): Redis 会将整个 Lua 脚本作为一个整体执行。在脚本运行期间,Redis 不会执行其他任何客户端的命令。不用加任何分布式锁,天然防止并发冲突。
  2. 减少网络开销 (Reduce Network Overhead): 原本需要 3 次请求(请求头 + 网络传输 + 解析)的操作,现在合并成 1 次请求发送给 Redis,极大地降低了网络延迟。

2. 从'菜鸟'到'老鸟':EVAL vs SCRIPT LOAD

执行 Lua 脚本有两个命令,代表了两种不同的段位。

菜鸟写法:每次都传整个脚本 (EVAL)
# 语法:EVAL script numkeys key [key ...] arg [arg ...]
EVAL "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end" 1 mylock myrandomvalue 
  • 痛点: 你的业务逻辑脚本可能长达上百行(几 KB)。如果秒杀时 QPS 是 10 万,你每秒钟都要在网络上传输 10 万次 * 几 KB 的一段重复字符串,这会瞬间打满服务器的网卡带宽。
老鸟写法:脚本预加载 (SCRIPT LOAD + EVALSHA)

为了解决带宽浪费,Redis 提供了'缓存脚本'的机制。

第一步:预加载脚本 (SCRIPT LOAD) 在应用启动时(或者首次执行时),把长达百行的 Lua 脚本发送给 Redis。

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

Redis 编译这段代码,把它缓存在内存中,并返回一个 40 位的 SHA1 摘要(Hash 值),例如:b8059ba43fd6bb4979e8eb3c1a84c8fb23eabcc1。

第二步:通过 Hash 值执行 (EVALSHA) 真正的高并发请求到来时,客户端只需要发送那个短小精悍的 SHA1 值即可!

EVALSHA b8059ba43fd6bb4979e8eb3c1a84c8fb23eabcc1 1 mylock myrandomvalue 
  • 收益: 网络传输量从几 KB 骤降到只有 40 个字节,完美榨干网卡性能。

3. 生产环境的'兜底逻辑' (NOSCRIPT 错误)

脚本预加载非常棒,但有一个坑:Redis 重启,或者执行了 SCRIPT FLUSH,缓存的脚本会丢失。 此时如果你继续用 EVALSHA 去请求,Redis 会报错:NOSCRIPT No matching script. Please use EVAL.

标准工业级代码的执行流水线:

  1. 客户端带着 SHA1 尝试执行 EVALSHA。
  2. 如果成功,直接返回。
  3. 如果捕获到 NOSCRIPT 异常,客户端立刻降级,使用原本的长脚本执行一次 EVAL。
  4. EVAL 执行成功的同时,Redis 会自动将该脚本重新缓存。后续的请求就又能开心地用 EVALSHA 了。 (注:主流的 Redis 客户端,如 Java 的 Redisson/Jedis,Go 的 go-redis,底层已经自动帮你封装了这个'EVALSHA 失败后降级 EVAL'的逻辑。)

4. 三大高频实战场景与 Lua 源码

场景一:分布式锁的原子释放 (Check-and-Delete)

痛点: 释放锁时,必须判断'这把锁是不是我自己加的'(防止误删别人的锁)。查锁和删锁必须原子化。 Lua 脚本:

-- KEYS[1]: 锁的 key
-- ARGV[1]: 当前线程的 UUID
if redis.call("GET", KEYS[1]) == ARGV[1] then
    return redis.call("DEL", KEYS[1])
else
    return 0
end
场景二:极速秒杀扣库存 (Check-and-Deduct)

痛点: 高并发下判断库存是否大于购买量,够才扣除。 Lua 脚本:

-- KEYS[1]: 库存 key
-- ARGV[1]: 需要扣减的数量
local stock = tonumber(redis.call('GET', KEYS[1]))
local num = tonumber(ARGV[1])
if (stock == nil) then
    return -1 -- 商品不存在
end
if (stock >= num) then
    redis.call('DECRBY', KEYS[1], num)
    return 1 -- 扣减成功
else
    return 0 -- 库存不足
end
场景三:滑动窗口限流 (Sliding Window Rate Limiter)

痛点: 限制某个 IP 在 60 秒内只能访问 100 次,使用 ZSet 记录时间戳,清理旧数据和统计当前数据必须原子完成。 Lua 脚本:

-- KEYS[1]: rate_limit:ip:192.168.1.1
-- ARGV[1]: 当前时间戳 (毫秒)
-- ARGV[2]: 窗口大小 (例如 60000 毫秒)
-- ARGV[3]: 限制次数 (例如 100)
local current_time = tonumber(ARGV[1])
local window_size = tonumber(ARGV[2])
local max_requests = tonumber(ARGV[3])
local window_start = current_time - window_size

-- 1. 清除窗口外的旧数据
redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, window_start)

-- 2. 统计当前窗口内的请求数
local current_requests = redis.call('ZCARD', KEYS[1])

if current_requests < max_requests then
    -- 3. 未超限,记录本次请求 (Member 可以用唯一 UUID 防重叠,Score 用时间戳)
    -- 这里简单用时间戳拼接一个随机后缀作为 Member
    redis.call('ZADD', KEYS[1], current_time, current_time .. tostring(current_requests))
    -- 设置一下整体过期时间,防止冷数据占用内存
    redis.call('PEXPIRE', KEYS[1], window_size)
    return 1 -- 放行
else
    return 0 -- 限流
end

5. 避坑指南:Lua 脚本的致命红线

  1. 绝对不要写慢脚本! 由于 Redis 单线程执行 Lua,如果你的脚本里有一个死循环,或者涉及大量的 keys 遍历(耗时超过毫秒级),整个 Redis 节点会被死死卡住,任何其他的 GET/SET 命令都进不来!
  2. 避免使用外部依赖和随机状态。 在 Redis 5.0 之前(按脚本复制模式),如果你的脚本里调用了获取系统时间的函数,或者生成了随机数,会导致主从节点执行结果不一致。虽然 5.0 之后引入了'效果复制'(只复制脚本引起的写命令),但尽量让脚本保持**纯函数(确定性)**依然是好习惯。
  3. 合理区分 KEYS 和 ARGV。 在集群模式(Redis Cluster)下,Redis 必须在解析命令时就知道这条脚本会操作哪些 Key,以便进行路由。所以,所有会被脚本操作的 Key,必须明确放到 KEYS 数组里传进去,绝对不能在脚本里面硬编码拼装 Key。

目录

  1. 1. 核心原理:为什么是 Lua?
  2. 2. 从“菜鸟”到“老鸟”:EVAL vs SCRIPT LOAD
  3. 菜鸟写法:每次都传整个脚本 (EVAL)
  4. 语法:EVAL script numkeys key [key ...] arg [arg ...]
  5. 老鸟写法:脚本预加载 (SCRIPT LOAD + EVALSHA)
  6. 3. 生产环境的“兜底逻辑” (NOSCRIPT 错误)
  7. 4. 三大高频实战场景与 Lua 源码
  8. 场景一:分布式锁的原子释放 (Check-and-Delete)
  9. 场景二:极速秒杀扣库存 (Check-and-Deduct)
  10. 场景三:滑动窗口限流 (Sliding Window Rate Limiter)
  11. 5. 避坑指南:Lua 脚本的致命红线
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • AI 提示词实战指南:覆盖 20+ 高频场景与技巧
  • 豆包高效学习:AI 时代教育破局与认知升级指南
  • 从 Web 到 API 驱动:Django REST Framework 重构智能合同审查系统
  • 字节跳动前端一面深度解析:React 原理与浏览器渲染
  • OpenClaw 构建飞书 AI 办公机器人:本地 Ollama 接入与 Skills 自动化
  • DooTask 轻量级项目管理与 AI 协同功能解析
  • MySQL 数据类型核心指南:选型、实战与避坑
  • Seq2Seq 模型实战:ScheduledEmbeddingTrainingHelper 原理与使用
  • SpringBoot 4.0 新特性整合项目实战应用详解
  • Cursor 集成 MCP 服务实战指南:从配置到自动化任务执行
  • pyenv-win Python 多版本管理实战与效率优化方案
  • JavaScript 调试技巧与实用工具指南
  • 基于 GLM4.7 的 Claude Code GitHub 代码自动审查
  • AI 对话页流式处理架构:Web Streams 与 Fetch API 实践
  • AIGC 产品经理工作职责与职位要求解析
  • Spring Boot 全局异常处理与日志监控实战
  • Linux 命名管道(FIFO)通信:原理与跨进程实战
  • Java 大数据量 Excel 导入导出实现方案
  • Rust 异步微服务架构最佳实践与反模式规避
  • Mac mini M4 部署 OpenClaw + Ollama 本地大模型接入飞书机器人

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online