李锋镝的博客

  • 首页
  • 时间轴
  • 说说
  • 读书观影
  • 系列
  • AI工具集
  • 论坛
  • 友圈
    • 友链
    • 朋友圈
    • 游客中心
  • 留言
    • 留言
    • 走心评论
  • 关于
    • 关于本站
    • Now
    • 每日心情
    • 网站地图
    • 网站统计
    • 赞助
  • 星球穿梭
  • 🚇开往
静亦定 动亦定
惟坚韧者始能遂其志
  1. 首页
  2. 后端
  3. 正文

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

2026年9月23日 123 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日

岁月同一天 11 月 7 日

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

  • AI
    1 年前 2025年11月7日
    DeepSeek本地部署全攻略:从环境搭建到高级应用,打造专属 AI 助手

    DeepSeek 作为国产开源大模型的佼佼者,凭借出色的推理能力、代码生成效率和中文支持,成为很多开发者本地部署的首选。…

相关文章
  • Redis实例CPU飙高至90%:从根源排查到彻底解决的全攻略2025年10月16日
  • Redis7.x&8.x 全新进阶系列(01):划时代升级总览——从6.x到7.x/8.x全版本变革全景2026年9月23日
  • 项目中不用 redis 分布式锁,怎么防止用户重复提交?2025年9月3日
  • 深度解析多级缓存架构:从设计到落地,彻底解决数据一致性难题2025年11月4日
  • Redis的主从同步及Redis Cluster(集群)下的高可用2021年4月16日

李锋镝

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

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

文章评论

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

惟坚韧者始能遂其志。

听点儿音乐吧 朋友~
文章目录
最新 热点 随机
最新 热点 随机
给服务器加了WAF 又一个逆天改命的人:小小徐 AI的生成能力等于生产力吗? NGINX严重漏洞(CVE-2026-15176、CVE-2026-93479、CVE-2026-97205) Kratos-plus v1.1.27更新说明 php2shell远程执行漏洞修复(CVE-2026-109618 和 CVE-2026-109619)
给主题增加了游客中心功能心气没了,干什么都不得劲我觉得是时候公布我的导航网站了给服务器加了WAF游本昌去世关于主题加载速度优化的一点儿小演进
几款Java开发者必备常用的工具,准点下班不在话下 StarUML4.0破解文件 为什么说不可信的Wi-Fi不要随便连接? 技术人员转团队管理该怎么做 Redis 不只是缓存:8 大实战场景 + 深度避坑指南,从入门到架构师级应用 妹妹的画【2019.07.05】
最近评论
fairy 发布于 18 小时前(11月06日) 机器人快来我这里吧!
李锋镝 发布于 18 小时前(11月06日) 都是机器人,防不住
李锋镝 发布于 18 小时前(11月06日) 是的,太烦了
李锋镝 发布于 18 小时前(11月06日) 可以同一台服务器
fairy 发布于 20 小时前(11月06日) 这流量,好厉害啊。我一年都没那么多
标签聚合
Spring Redis Claude 架构 数据库 K8s JAVA SpringBoot JVM IDEA 日常 SQL MySQL Theme 分布式 AI编程 多线程 WordPress AI ElasticSearch
友情链接
  • 彬红茶日记
  • 志文工作室
  • Mr.Sun的博客
  • 九仞之行
  • 瓦匠个人小站
  • Serendipity
  • Honesty
  • 知向前端
  • 韩小韩博客
  • 搬砖日记
  • 刘永博客
  • lijie blog
  • 林羽凡
  • 老张博客
  • 韩情脉脉
  • 哥斯拉
  • sssr7844的博客
  • 皮皮社
  • 懋和道人

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号