Redis Functions 介绍之二

首先,让我们先回顾一下上一篇讲的在Redis Functions中关于将key的名字作为参数和非key名字作为参数的区别,先看下面的例子。首先,我们先在一个Lua脚本文件mylib.lua中定义如下的库和函数。

复制代码
//--------------------mylib.lua 文件开始 ----------- //
#!lua name= mylib

local function my_hset(keys, args)
    local hash = keys[1]
    local time = redis.call('TIME')[1]     //这一行的目的是执行TIME命令得到当前的服务器时间
    return redis.call('HSET', hash, '_last_modified_', time, unpack(args))
end

redis.register_function('my_hset', my_hset)
//--------------------mylib.lua 文件结束 ----------- //

然后我们在命令行中运行如下命令:

复制代码
$ cat mylib.lua | redis-cli -x FUNCTION LOAD"mylib"

当我们再运行如下FCALL命令时候,我们会得到如下的结果:

复制代码
redis> FCALL my_hset 1 myhash_key first_field "first value" second_field "second value"(integer) 3

上面命令的my_hset是函数名,1代表第一个参数myhash_key是key名,后面的first_field "first value" second_field "second value" 都是这个key: myhash_key的field:value对我们通过在Redis客户端运行 HGETALL myhash_key可以查看到相对应的结果:

复制代码
redis> HGETALL myhash_key
1) "_last_modified_"
2) "1659536487"
3) "first_field"
4) "first value"
5) "second_field"
6) "second value"

好了,在复习完了上一篇Redis Functions的内容之后,我们可以看到上面的例子中mylib库中只包含了一个函数my_hset, 事实上在一个库中可以包括多个函数,在下面的例子中我们要添加另外2个function:my_hgetall 与 my_hlastmodified.其中,函数my_hgetall相当于在redis 客户端执行hgetall命令,而my_hlastmodified函数返回的是传入的key中_last_modified这一个field的值。首先,让我们在mylib.lua中添加这两个function

复制代码
local function my_hgetall(keys, args)
    local hash = keys[1]
    local res = redis.call('HGETALL', hash)
    return res
end

local function my_hlastmodified(keys, args)
    local hash = keys[1]
    return redis.call('HGET', keys[1], '_last_modified_')
end

然后,在mylib中注册这两个函数。

复制代码
redis.register_function('my_hgetall', my_hgetall)
redis.register_function('my_hlastmodified', my_hlastmodified)

因为我们要在mylib库中新增两个函数,所以我们需要运行如下的命令:

复制代码
$ cat mylib.lua | redis-cli -x FUNCTION LOAD REPLACE"mylib"

接下来我们运行my_hgetall函数与my_hlastmodified

复制代码
redis> FCALL my_hgetall 1 myhash_key
1) "_last_modified_"
2) "1659536487"
3) "first_field"
4) "first value"
5) "second_field"
6) "second value"

redis> FCALL my_hlastmodified 1 myhash_key
"1659536487"

最后我们可以通过FUNCTION LIST命令来查看当前所有的Redis Function的库及其包含的函数信息。下面我们来查看一下当前库mylib中包括的3个function的信息。

复制代码
127.0.0.1:6381> FUNCTION list
1) 1) "library_name"
   2) "mylib"
   3) "engine"
   4) "LUA"
   5) "functions"
   6) 1) 1) "name"
         2) "my_hset"
         3) "description"
         4) (nil)
         5) "flags"
         6) (empty array)
      2) 1) "name"
         2) "my_hlastmodified"
         3) "description"
         4) (nil)
         5) "flags"
         6) (empty array)
      3) 1) "name"
         2) "my_hgetall"
         3) "description"
         4) (nil)
         5) "flags"
         6) (empty array)

在Redis的官方文档中,我们发现我们可以在库中定义一个function供库中的其他函数使用,在官方的使用说明,它给出了一个例子用于错误检测。我们下面来看看这个例子。定义一个check_keys的函数,用来检测客户端调用的redis function的函数输入的key是否存在并且只有一个。

复制代码
local function check_keys(keys)
    local error = nil
    local nkeys = table.getn(keys)
    if nkeys == 0 then
        error = 'Hash key name not provided'
    elseif nkeys > 1 then
        error = 'Only one key name is allowed'
    end

    if error ~= nil then
        redis.log(redis.LOG_WARNING, error);
        return redis.error_reply(error)
     end
     return nil
end

相应的,我们也需要修改已经有的3个函数,让他们去调用新增的check_keys函数

复制代码
local function my_hset(keys, args)
    local error = check_keys(keys)
    if error ~= nil then
        return error
    end

    local hash = keys[1]
    local time = redis.call('TIME')[1]
    return redis.call('HSET', hash, '_last_modified_', time, unpack(args))
end

local function my_hgetall(keys, args)
    local error = check_keys(keys)
    if error ~= nil then
        return error
    end

    local hash = keys[1]
    local res = redis.call('HGETALL', hash)
    return res
end

local function my_hlastmodified(keys, args)
     local error = check_keys(keys)
     if error ~= nil then
         return error
     end

     local hash = keys[1]
     return redis.call('HGET', keys[1], '_last_modified_')
end

这里要注意一点,在上面的例子中,如果我们运行FCALL:

复制代码
redis> FCALL my_hgetall 1 myhash_key
1) "_last_modified_"
2) "1659536487"
3) "first_field"
4) "first value"
5) "second_field"
6) "second value"

我们可以看到运行是正常的,但是如果我们运行FCALL_RO:

复制代码
redis> FCALL_RO my_hgetall 1 myhash_key
(error) ERR Can not execute a script with write flag using *_ro command.

我们会收到一个错误。这是为什么呢?事实上,函数my_hgetall的目的只是从数据中读取数据,并没有写操作。但是当我们定义它的时候,并没有标注它是只读属性

复制代码
local function my_hgetall(keys, args)
    local hash = keys[1]
    local res = redis.call('HGETALL', hash)
    return res
end

所以这个时候,当我们用命令FCALL_RO就会返回一个错误:Can not execute a script with write flag using *_ro command所以为了可以让FCALL_RO执行一个只读属性的函数,我们需要对my_hgetall的注册方式进行一下修改:

复制代码
redis.register_function{
    function_name='my_hgetall',
    callback=my_hgetall,
    flags={ 'no-writes' }
}

这个时候,我们再运行FCALL_RO命令时候,我们就会得到正确的结果了

复制代码
redis> FCALL_RO my_hgetall 1 myhash_key
1) "_last_modified_"
2) "1659536487"
3) "first_field"
4) "first value"
5) "second_field"
6) "second value

Redis functions是如何在集群中运行

在非集群状态时候,如果有主从节点,那么主节点上的function会自动地被复制到从节点,这与function创建的初衷也是一致的:Redis认为Functions是数据库的一部分,是可以持久化的,并且也是可以复制的。但是,如果function是存在于集群的节点中,那么就有一些不一样的特性了。Redis function无法被自动地加载到集群中的所有节点,而需要被管理员手动地将function加载到集群中的每个节点中。要加载Redis function在集群中的节点,可以运行如下的命令:

复制代码
redis-cli --cluster-only-masters --cluster call host:port FUNCTION LOAD [REPLACE] function-code

如果运行命令:

复制代码
redis-cli --cluster add-node

那么这个时候集群中已经存在的节点会将已经加载的function自动地传播到新加入集训的节点。

使用Redis Function的其他的注意事项

如果一个已经加载Redis Function的节点需要重启,那么在重启之前建议先运行命令:

复制代码
redis-cli --functions-rdb

这样会将已经存在于节点上的Redis function存到一个RDB文件中,当这个节点重启之后可以直接加载这个rdb文件,就不需要再手工加载每一个function了,大大地节省了时间和减少错误机率。本篇文章引用了部分Redis官方文档的例子, 大家可以从链接去查看和运行一下。

https://redis.io/docs/manual/programmability/functions-intro/

下一篇,我们开始分析Redis function的源码,让我们深入探讨一下这个Redis新特性内部是如何执行,欢迎继续关注Redis Functions系列文章。

最后,感谢Redis社区的所有贡献者的努力工作,及所有对Redis感兴趣的开发者的支持。

相关推荐
Chan1611 分钟前
【 Java八股文面试 | Redis篇 缓存问题、持久化、分布式锁 】
java·数据库·redis·后端·spring·缓存·面试
百***62853 小时前
Spring Boot 3.X:Unable to connect to Redis错误记录
spring boot·redis·后端
hoiii1877 小时前
设置Redis在CentOS7上的自启动配置
数据库·redis·缓存
爬山算法7 小时前
Redis(122)Redis的版本更新如何进行?
数据库·redis·bootstrap
thekenofdis14 小时前
Lua脚本执行多个redis命令提示“CROSSSLOT Keys in request don‘t hash to the same slot“问题
redis·lua·哈希算法
oneslide18 小时前
Kubernetes环境部署Redis集群
redis·容器·kubernetes
是垚不是土20 小时前
构建高可用Redis:哨兵模式深度解析与Nacos微服务适配实践
redis·微服务·bootstrap
小坏讲微服务1 天前
Spring Cloud Alibaba Gateway 集成 Redis 限流的完整配置
数据库·redis·分布式·后端·spring cloud·架构·gateway
hoiii1871 天前
挂载配置文件以Docker启动Redis服务
redis·docker·eureka