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

Redis lua 执行性能优化

注意:以下仅基于个人经验总结

注意:以下仅基于个人经验总结

注意:以下仅基于个人经验总结

Redis中可在 lua 中实现多命令的原子化执行,一般情况下性能够用,但是脚本较为复杂且还需更高的性能则需要对 lua 脚本进行优化,以下是我的 lua 性能优化经验,代码以 C# .NET8 为例,Redis 版本为 8.2.0,系统为 Window10,CPU 10 代 i3 主频 3.7 GHZ

其实主要就三点:

  1. 使用 Pipeline 串多个 lua 批量执行减少网络传输
  2. lua 内部也尽量进行批量执行
  3. 单次 lua 内部对中间数据进行缓存

1. 使用 Pipeline 优化

这个是比较常用的优化手段,使用 Pipeline 可以一次打包减少网络往返传输,Pipeline 批次大小需要自己实测调整到一个比较合适的范围就行,还有利用 Redis 的 lua hash 缓存功能避免传输整个脚本进一步减少网络消耗,不过如果你的 Redis 和程序部署在同一台服务器上的话其实没有什么变化。

如果使用多个连接的话还可以开启redis的io多线程功能,有一定的io优化,对lua执行无帮助,执行仍然是单线程

2. lua内部批量执行

这里主要是命令上的优化,例如 set 命令,可以改为 mset 批量操作,get 也改成 mget 批量取值

3. 单次 lua 内部对中间数据进行缓存

这里需要说明的一点是redis.call的调用开销很大,即使是在 lua 内部执行redis.call命令操作 Redis,如果 call 命令调用多了也会严重拉低执行性能,性能与redis.call次数负相关,个人经验是lua脚本内部不超过 5个 call 命令比较好,最好是三个以内。
以下为测试程序和结果:

只要不调用redis.call命令,纯 lua 执行还是非常快的基本上可以忽略不记,所以可以把一些数据缓存在 lua 内存中,注意 Redis 的 lua 环境是沙箱,每次执行 lua 脚本都是重新初始化一个 lua 环境,所以缓存仅限于当前执行时有效

可以将lua执行的参数也进行批量收集,然后传递给 lua 脚本,所有参数在同一个脚本里面进行执行,这样就可以将一些中间数据直接在当前 lua 内进行缓存减少redis.call的调用提升执行性能。

如一个简单的计费功能(假设余额充足):
需要两个步骤:

  1. 调用redis.call命令取出当前值
  2. 扣减,如果扣减后余额大于0则调用redis.call更新余额并返回1,否则返回0表示失败(lua中返回false解析出来是null)
    这里就不可避免的有两次redis.call调用,如果有一百万次扣费,就会有两百万次的redis.call调用

如果改成lua参数批量化执行将会变成这样(示例简化版本):
参数:

KEYS[1] = balanceKey ARGV: userId1 amount1 userId2 amount2 ...

lua:

localkey=KEYS[1]-- 本地余额缓存localbalanceCache={}localresult={}localuserIds={}localuserIdsExistsMap={}-- 收集去重userIdfori=1,#ARGV,2dolocaluserId=ARGV[i]ifnotuserIdsExistsMap[userId]thenuserIdsExistsMap[userId]=trueuserIds[#userIds+1]=userIdendend-- 从redis取当前数据缓存在本地localvalues=redis.call('HMGET',key,unpack(userIds))fori=1,#valuesdolocaluserId=userIds[i]localbalance=values[i]-- 缓存余额到本地(若redis中有值,无值代表非法参数,跳过)ifbalancethenbalanceCache[userId]=tonumber(balance)endend-- 计费localj=1;fori=1,#ARGV,2dolocaluserId=ARGV[i]localamount=ARGV[i+1]localoldBalance=balanceCache[userId]ifnotoldBalancethenresult[j]=falseelselocalnewBalance=oldBalance+amountifnewBalance>0thenbalanceCache[userId]=newBalance result[j]=trueelseresult[j]=falseendendj=j+1end-- 批量写回localargs={}foruserId,balanceinpairs(balanceCache)doargs[#args+1]=userId args[#args+1]=balanceendif#args>0thenredis.call("HMSET",key,unpack(args))endreturnresult

在内部缓存后,一次lua执行实际上只需要执行两次redis.call极大节省开销
如果不使用上例手段优化,执行该示例的余额更新lua性能大约是3w/s,使用了优化后性能约20w/s
(数值只是大概值写了很久了忘了当时具体数值)
虽然看着不优化也有3w/s,但是这是只有这一个lua的消耗,如果还要其他高并发的占用性能的脚本,把3w均分下来其实性能很拉跨

另外对于 一些复杂 lua 来说,在使用参数批量收集时再使用集群似乎不是非常好的选择个人感觉限制较大,在执行前会根据你的redis key进行分片分配到某个实例上,但是lua内部可能有key是动态拼接,参数中的key也可能不会分配到同一个实例,虽然可以通过使用{}形式将key指定为分片依据,但是一些复杂的lua脚本内部可能还使用了不少别的key,这些key可能不在当前实例,或者最终可能仍然打到同一个实例上,仅个人观点

4. 程序方面的优化

我做了两层批处理收集
第一层是批处理收集lua参数,收集到指定数量(或达到指定最长收集时间)的参数后再提交给pipeline,这收集的一批参数就是执行一次lua的参数。
第二层就是Pipeline,提交Pipeline的执行前的时候也有一个批处理收集,收集到指定数量后(或达到指定最长收集时间)再一次性pipeline处理

另外,批处理收集时还可以根据数据特点和redis key的分布情况进行分组收集,例如将具有相同用户id的参数收集,然后作为一次lua执行,这样能更好的利用lua内缓存以减少redis.call的调用次数从而提升性能。

至于结果回调是程序内部对每次执行维护了一个唯一id,通过该id找到对应的调用方回调结果,调用方提交后异步等待即可

http://www.jsqmd.com/news/472163/

相关文章:

  • 记录踩过的坑-金蝶云·苍穹平台-流程开发
  • sse哈工大C语言编程练习41
  • 可转债择时策略复现
  • Microsoft SQL Server 2025 RTM GDR CU3 (2026 年 3 月安全更新 | 累计更新)
  • 伏羲天气预报镜像免配置:预装Gradio/xarray/onnxruntime-gpu全栈环境
  • 主流渲染软件有哪些?行业优选云渲染该怎么选?
  • 个人创作者如何用?IndexTTS 2.0桌面版部署实战推荐
  • 为什么选择dash-bootstrap-components?5大理由让你的Python可视化更出众
  • 2026年江苏电力/干式/能效/油浸式变压器采购推荐厂商:高能效时代下的选型与技术趋势解读 - 2026年企业推荐榜
  • Z-Image-Turbo实战落地:独立开发者用其打造微信小程序AI头像生成后端服务
  • Microsoft SQL Server 2022 RTM GDR CU24 (2026 年 3 月安全更新 | 累计更新)
  • 1004: 惠民工程
  • 详解Mach-O(三十一)Mach-O __nl_symbol_ptr节
  • 影墨·今颜多分辨率适配教程:竖版9:16/方版1:1/横版16:9精准控制
  • 详解Mach-O(三十二)Mach-O __mod_init_func节
  • 2026年高温沥青/包覆沥青/特种沥青厂家推荐:煤沥青、改质沥青、球状沥青专业供应商选型指南 - 品牌推荐官
  • RMBG-2.0企业级部署:Nginx反向代理+JWT鉴权,构建安全可控抠图SaaS
  • Cogito 3B真实生成效果展示:中英混输、复杂逻辑链、多步工具调用
  • react-bootstrap-table2列定义完全指南:自定义表格列的终极技巧
  • vscode-portfolio高级技巧:优化性能与提升用户体验
  • 油耗降至3.3L以下 HORSE H12概念发动机亮相
  • 万象熔炉 | Anything XL纯本地推理教程:无网无上传隐私安全生成方案
  • OpenClaw 卸载不干净?macOS / Windows / Linux 彻底清理指南
  • 2026机器人智能焊接系统选型指南,焊接协作机器人供应商怎么选 - 品牌2026
  • ScreenCat开发实战:从零开始构建你的第一个WebRTC屏幕共享应用
  • 脑机接口1.5亿融资背后的硬科技投资逻辑:超声波路径如何重构人机交互边界?
  • STEP3-VL-10B图文理解教程:支持中文界面截图+自然语言提问的实操
  • EagleEye部署教程:Kubernetes集群中EagleEye服务的水平扩展与健康检查
  • web-socket-js实战教程:从环境搭建到消息收发全流程
  • StructBERT轻量级模型部署教程:离线环境无网安装与依赖包打包