李锋镝的博客

  • 首页
  • 时间轴
  • 说说
  • 系列文章
  • AI工具集
  • 论坛
  • 左邻右舍
    • 左邻右舍
    • 朋友圈
    • 游客中心
  • 留言
    • 留言
    • 走心评论
  • 关于
    • 关于本站
    • Now
    • 每日心情
    • 网站地图
    • 网站统计
    • 赞助
  • 星球穿梭
  • 🚇开往
白雪成红
惟坚韧者始能遂其志
  1. 首页
  2. 后端
  3. 正文

Redis7+&8.X 全新进阶系列(02):Redis Functions 详解——替代Lua脚本的官方轻量化函数方案

2026年9月23日 约 2,786 字10 分钟 71 0 0
合集:Redis第 2 篇 / 共 2 篇
  1. 1Redis7.x&8.x 全新进阶系列(01):划时代升级总览——从6.x到7.x/8.x全版本变革全景
  2. 2Redis7+&8.X 全新进阶系列(02):Redis Functions 详解——替代Lua脚本的官方轻量化函数方案当前

参考文档:Redis 7.0 Release Notes、官方 Functions 文档、redis/src/function/ 源码模块
验证环境:Redis 7.0.15

前言

在 Redis 7.0 正式发布之前,Lua 脚本是 Redis 实现原子复合操作的唯一内置方案。大量业务依赖 Lua 实现批量读写、原子校验、复杂计算逻辑,但在长期生产实践中,Lua 的缺陷不断暴露:脚本分散无法统一管理、缺少版本控制、调试困难、权限粗粒度、脚本缓存与集群同步存在隐性风险。

Redis 7.0 引入 Redis Functions,作为官方原生脚本方案,定位并不是简单替换 Lua,而是在保留原子执行、服务端运算能力的前提下,解决 Lua 在运维、权限、生命周期管理上的痛点。

本篇我们会对比 Lua 的历史痛点、Functions 的设计思路、完整使用示例、Lua vs Functions 差异、源码核心逻辑,以及生产环境的迁移与风险。

一、Lua 脚本在 Redis 6.x 的核心痛点

Lua 脚本核心优势是单脚本原子执行,但这些原生短板是架构层面无法修复的:

  1. 脚本无持久化与版本管理
    业务代码每次都通过 EVAL/EVALSHA 上传脚本,脚本仅保存在节点内存。集群扩容、节点重启后脚本丢失,业务需要客户端重新推送脚本;没有版本标记,多人维护容易出现脚本版本错乱。
  2. 权限管控粒度粗糙
    ACL 只能禁止/允许执行 EVAL 整条命令,无法单独限制某一段脚本,一旦开放 EVAL,脚本内部可以调用任意Redis命令,存在高危操作风险。
  3. 调试与可观测性差
    Lua 的 print 输出只能写入服务端日志,无法返回给客户端;脚本报错信息简陋,很难定位线上问题。
  4. 集群同步缺陷
    使用 EVALSHA 在集群场景下,存在脚本哈希在不同节点不一致的问题;跨槽脚本本身就不被支持,但Lua没有提前校验机制。
  5. 复用性差
    逻辑无法封装成命名函数,公共逻辑只能复制粘贴到各个业务脚本中,维护成本高。

注意:Redis 没有废弃Lua,只是推荐新业务优先使用 Functions;老Lua脚本可以继续运行,属于兼容保留。

二、Redis Functions 设计思路

Redis Functions 的核心设计目标:将脚本从“客户端临时提交的代码片段”升级为“服务端托管、命名、版本化、可权限隔离的函数”。

核心设计要点:

  1. 函数在服务端持久托管:使用 FUNCTION LOAD 将代码上传到Redis,函数会持久化到RDB/AOF,节点重启自动加载。函数拥有唯一名称,客户端只需要调用名字即可执行。
  2. 库+函数的两层封装:代码以**library(库)**为单位加载,一个库可以包含多个函数;库支持版本标记。
  3. 独立沙箱环境:Functions 的执行沙箱与Lua隔离,可单独对函数配置ACL权限,控制函数内部能调用哪些Redis命令。
  4. 统一生命周期:支持加载、删除、列举、转储函数,运维侧可以统一管理所有服务端注册逻辑。
  5. 保留原子语义:单个函数调用依然是单线程原子执行,和Lua一样,执行期间阻塞其他命令,长耗时函数同样会造成主进程阻塞。

重要取舍:Functions 并没有解决“长脚本阻塞Redis主线程”的问题。无论是Lua还是Functions,都是在Redis单线程事件循环内执行,禁止在函数内编写重计算、长时间阻塞逻辑,这是官方明确的约束。

三、完整使用示例

环境:Redis 7.0 单机,redis-cli 客户端

3.1 FUNCTION LOAD:加载函数库

函数代码使用 #!lua 声明语言(7.0仅原生支持Lua作为函数语言),一个library包含多个函数。

#!lua name=mylib
-- 库名称:mylib
local function hello(keys, args)
    -- keys: 传入的key数组
    -- args: 传入的参数数组
    redis.call('SET', keys[1], args[1])
    return "ok, value=" .. args[1]
end

local function get_val(keys, args)
    return redis.call('GET', keys[1])
end

-- 注册函数名称
redis.register_function('hello', hello)
redis.register_function('get_val', get_val)

使用 FUNCTION LOAD 加载:

FUNCTION LOAD "#!lua name=mylib\nlocal function hello(keys, args)\nredis.call('SET', keys[1], args[1])\nreturn \"ok, value=\" .. args[1]\nend\nlocal function get_val(keys, args)\nreturn redis.call('GET', keys[1])\nend\nredis.register_function('hello', hello)\nredis.register_function('get_val', get_val)"

加载成功会返回库的SHA哈希。

若库已存在,使用 FUNCTION LOAD REPLACE 覆盖更新版本。

3.2 FCALL 调用函数

语法:FCALL <funcname> <numkeys> [key1 key2 ...] [arg1 arg2 ...]

# 调用 hello 函数,1个key,参数为 world
FCALL hello 1 testkey "world"

# 读取值
FCALL get_val 1 testkey

输出:

1) "ok, value=world"

3.3 查看已加载函数

FUNCTION LIST

返回库名、函数名称、源码、描述信息。

3.4 删除函数库

FUNCTION DELETE mylib

3.5 持久化验证

执行 BGSAVE,重启Redis,重新连接,直接 FCALL get_val 1 testkey,无需重新加载函数,函数从RDB自动恢复。

3.6 序列化导出/导入

# 导出全部函数
FUNCTION DUMP

# 导入函数
FUNCTION RESTORE <dump_data>

适合多环境同步函数。

四、Lua VS Redis Functions 全方位对比

对比维度Lua脚本(EVAL/EVALSHA)Redis Functions
存储位置节点内存,重启丢失;依赖客户端推送RDB/AOF持久化,服务端托管,重启自动加载
调用方式传入完整代码片段 / sha哈希按函数名调用
版本管理无原生版本标记以library为单位,支持REPLACE更新
权限控制ACL只能全局开关EVAL,无法隔离脚本支持ACL针对函数单独授权,限制内部命令
代码复用无封装,代码片段分散库内多个函数,公共逻辑可复用
集群同步EVALSHA存在哈希不一致风险函数随RDB/AOF同步,集群节点自动加载
调试能力print输出到服务端日志同样lua沙箱,7.x小幅增强报错信息
原子性原子执行,阻塞主线程原子执行,阻塞主线程
适用场景一次性临时脚本、简单测试业务长期稳定复用的服务端逻辑

破坏性变更提醒:Redis 7.2 废弃了Lua脚本中的print函数,但Functions内的Lua代码不受这个废弃影响,这是官方文档明确区分的行为。

五、核心源码解析(Redis 7.0,源码路径 src/function/)

Commit参考:7.0 分支 616225b

5.1 核心模块文件

  • function.c:函数管理入口,FUNCTION LOAD/FCALL/FUNCTION LIST 命令实现
  • function_lua.c:Lua语言沙箱实现,redis.register_function 注册逻辑
  • function.h:结构体定义 functionLib、function

5.2 关键数据结构

// 简化示意源码,非完整原始代码
typedef struct functionLib {
    sds name;               // 库名称
    sds code;               // 原始源码
    dict *functions;        // 库内函数字典 name -> function
    int engine;             // 执行引擎,7.0仅LUA
    long long version;
} functionLib;

typedef struct function {
    sds name;
    functionLib *lib;
    lua_func *func;         // lua闭包引用
} function;

Redis 在全局 server.function_libs 字典保存所有加载的library。

5.3 执行流程(FCALL)

  1. FCALL 命令入口 fcallCommand,解析函数名、keys、args;
  2. 在全局字典查找对应 function 对象;
  3. 执行ACL权限校验:检查当前用户是否有权限执行该函数,以及函数内部调用命令的权限;
  4. 进入Lua沙箱,把 keys、args 传入函数;
  5. 调用 redis.call() 执行Redis内置命令;
  6. 捕获返回值/异常,包装为Redis协议返回给客户端;
  7. 函数执行全程阻塞主线程,执行完毕释放沙箱上下文。

5.4 持久化逻辑

RDB持久化钩子:在RDB保存阶段,遍历 server.function_libs,序列化库源码、名称、函数列表写入RDB。
AOF持久化:加载函数的 FUNCTION LOAD 命令会写入AOF日志,重放AOF自动重建函数库。

源码关键注意点:函数的Lua沙箱和EVAL的Lua沙箱是两套独立虚拟机实例,环境变量互不共享。

六、生产落地、适用场景与踩坑清单

✅ 推荐使用场景

  1. 业务通用原子逻辑,多客户端复用(如库存扣减、状态流转、批量字段更新);
  2. 需要统一管控脚本,不希望业务客户端携带大量脚本代码;
  3. 希望做细粒度权限隔离,禁止业务脚本执行高危命令;
  4. Redis集群环境,不想处理EVALSHA哈希同步问题。

❌ 不推荐场景

  1. 一次性临时测试脚本,频繁修改调试;
  2. 执行耗时较长的计算逻辑(依然阻塞主线程);
  3. 需要动态高频更新函数(每次FUNCTION LOAD REPLACE会重载库,有成本)。

⚠️ 生产风险与踩坑

  1. 函数更新是全库替换:FUNCTION LOAD REPLACE 是替换整个library,不是单个函数;更新时原子替换,正在执行的旧函数会继续执行完毕。
  2. 集群场景下,跨槽key依然禁止:和Lua一样,FCALL传入的keys必须落在同一个hash slot,否则直接报错,不会自动转发。
  3. 权限最小化原则:不要给业务账号开放 FUNCTION LOAD / FUNCTION DELETE,这两个是高危运维命令,仅运维账号可用;业务账号只授予 FCALL。
  4. 版本回滚要提前备份:更新函数前,先 FUNCTION DUMP 导出备份,出错可以 FUNCTION RESTORE 回滚。
  5. 8.x兼容说明:Redis8.x保留Functions完整能力,没有移除;向量检索等新能力可以在Functions内部调用VSIM等命令。

迁移建议(Lua迁移到Functions)

  1. 筛选长期稳定、复用度高的Lua脚本,优先迁移;一次性临时脚本保留EVAL;
  2. 将多个相关脚本合并到同一个library,统一管理;
  3. 增加预发环境验证:加载函数、ACL权限、集群槽位校验;
  4. 保留监控:监控FCALL的执行耗时、报错次数,和Lua脚本监控对齐。

七、总结

Redis Functions 并不是性能上的巨大提升,而是运维与安全层面的升级。它把脚本从客户端的临时片段,转变为服务端托管的一等公民。

如果你在生产上被Lua脚本版本混乱、重启丢失、权限失控困扰,Functions 是7.x官方给出的标准解决方案。但是一定要记住底层不变的约束:单线程原子执行,长时间运算依然会阻塞Redis主线程。

本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可
分享到

Redis7+&8.X 全新进阶系列(02):Redis Functions 详解——替代Lua脚本的官方轻量化函数方案

也可使用浏览器菜单中的「分享」功能

微信扫一扫分享

标签: Redis Redis Functions 中间件
最后更新:2026年9月23日

岁月同一天 10 月 17 日

回望过去的今天,你在写什么

  • AI
    1 年前 2025年10月17日
    从技术本质到生态关联,一文吃透 AIGC、RAG、Agent、Function Call、MCP

    当ChatGPT帮你写周报、MidJourney生成海报、AI助手自动规划旅行时,你可能没意识到:这些看似连贯的智能体验…

  • 中间件
    1 年前 2025年10月17日
    10 个MQ高频业务场景深度解析

    在分布式系统开发中,很多开发者都会疑惑:“明明直接调用接口就能实现功能,为什么还要引入MQ这个‘中间商’?”其实,当系统…

  • 7 年前 2019年10月17日
    从零开始入门 K8s | Kubernetes 网络概念及策略控制

    一、Kubernetes 基本网络模型 本文来介绍一下 Kubernetes 对网络模型的一些想法。大家知道 Kuber…

相关文章
  • Redisson分布式锁的watch dog自动续期机制2023年1月5日
  • 还不懂Redis?看完这个故事就明白了!2020年10月19日
  • redis异常记录2023年4月17日
  • 深入理解Redis2019年11月22日
  • Redis的主从同步及Redis Cluster(集群)下的高可用2021年4月16日

李锋镝

既然选择了远方,便只顾风雨兼程。

打赏 点赞
< 合集上一篇
1234567891112131415161718192021222324252627282930313233343536373839404142434446474849505152535455575859606162636465666769727476777879808182858687909293949596979899
取消回复
…

文章评论

还没有评论,快来抢沙发吧~

Your time is limited, so don't waste it living someone else's life. Don't be trapped by dogma -- which is living with the results of other people's thinking. Don't let the noise of other's opinions drown out your own inner voice. And most important, have the courage to follow your heart and intuition. They somehow already know what you truly want to become. Everything else is secondary.

听点儿音乐吧 朋友~
文章目录
最新 热点 随机
最新 热点 随机
Markdown 的前世今生 讨论下XXL-JOB GLUE模式的安全问题 心气没了,干什么都不得劲 Kratos-plus v1.1.26版本更新说明 我觉得是时候公布我的导航网站了 代码是 AI 写的,生产事故谁背锅?
给主题增加了游客中心功能心气没了,干什么都不得劲我觉得是时候公布我的导航网站了游本昌去世关于主题加载速度优化的一点儿小演进给主题增加了Now、每日心情、年度回顾、岁月同一天、随机漫步等功能
什么是Meta Server? SpringBoot框架自动配置之spring.factories和AutoConfiguration.imports 为什么MySQL要“小表驱动大表” Spring Boot 指定外部配置文件的方式 OHCache使用 解决kubectl exec -it xxxx-service-bfbd45bb9-ktvzj bash -n bit error: exec [POD] [COMMAND] is not supported anymore. Use exec [POD] -- [COMMAND] instead See 'kubectl exec -h' for help and examples
最近评论
李锋镝 发布于 1 天前(10月16日) 其实语法很好记,而且也有很多markdown的文本编辑器
李锋镝 发布于 1 天前(10月16日) Typecho就是太轻量了,导致很多功能都没有
沐阳 发布于 1 天前(10月16日) 换了Markdown,好多代码都要重新记
不凡 发布于 1 天前(10月16日) 我搭建临时站点看了下,是有个预览,然后发现是主题对markdown编辑器做了“二开”加了一些自定义按...
不凡 发布于 1 天前(10月16日) 我开发的插件替代了原生附件管理,在附件管理显示最近上传的文件,点击缩略图就直接插入了,不过插入的仍然...
标签聚合
IDEA 数据库 多线程 ElasticSearch SpringBoot 架构 日常 MySQL SQL Theme Spring Claude JVM 分布式 WordPress AI AI编程 Redis JAVA K8s
友情链接
  • 志文工作室
  • lijie blog
  • Mr.Sun的博客
  • 搬砖日记
  • sssr7844的博客
  • Honesty
  • 瓦匠个人小站
  • 韩小韩博客
  • 韩情脉脉
  • 懋和道人
  • 刘永博客
  • Serendipity
  • 知向前端
  • 彬红茶日记
  • 九仞之行
  • 林羽凡
  • 老张博客
  • 哥斯拉
  • 皮皮社

COPYRIGHT © 2016-2026 lifengdi.com. ALL RIGHTS RESERVED.

lifengdi.com 十年之约虫洞

Domain age badge for lifengdi.com

Theme Kratos-plus By Dylan Li

津ICP备2024022503号-3

京公网安备11011502039375号