用FUNCTION LOAD注册一个带名字的函数库并用FCALL调用,对比裸EVAL脚本,看Functions解决了脚本缓存没有名字、不持久化这两个问题。
Redis Functions是比EVAL更晚引入的特性,核心区别是脚本有了名字和归属。
代码lib_code = """#!lua name=mylib redis.register_function('myincr', function(keys, args) local v = tonumber(redis.call('GET', keys[1]) or '0') v = v + tonumber(args[1]) redis.call('SET', keys[1], v) return v end) """ r.execute_command("FUNCTION", "LOAD", lib_code)
区别只是入口从"一段脚本文本"变成了"一个注册过的函数名"。
结果FUNCTION LOAD → "mylib" FCALL myincr 1 fn_counter 5 → 5 FCALL myincr 1 fn_counter 3 → 8 GET fn_counter → "8"
验证了myincr这个函数被调用了两次,第一次把fn_counter从空(当0处理)加到5,第二次再加3变成8——底层逻辑跟EVAL脚本没有区别,都是用redis.call去读写数据,Redis保证的原子性、单线程阻塞这些特性也完全一样,Functions只是给这套机制包了一层"有名字、可管理"的外壳。
这正是EVAL脚本天然做不到的——裸脚本只有一个SHA1哈希,没有名字,没法"列出我注册过哪些函数"。
结果FUNCTION LIST → [{'library_name': 'mylib', 'engine': 'LUA', 'functions': [{'name': 'myincr', ...}]}] 不带REPLACE重复LOAD同一份代码: → 报错: Library 'mylib' already exists FUNCTION DUMP → 178字节的二进制序列化数据
#!lua name=mylib),加载后的函数库会被持久化进RDB/AOF(普通EVAL脚本缓存不会),Redis重启后FUNCTION LIST依然能看到它,不需要客户端重新推送代码。Library already exists这个报错,以及需要显式加REPLACE参数才能覆盖更新,体现的正是"有名字的库"该有的版本管理语义——防止不同客户端不小心用不同代码覆盖了同一个函数名。
每一步都是本文上面用FUNCTION/FCALL命令跑出来的原始结果。
FUNCTION LOAD/LIST/DUMP / FCALL:注册、调用、查询一个函数库