• 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/interact/programmability/functions-intro/

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

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

  • 相关阅读:
    C\C++中数组指针和二维数组最强最简单粗暴深刻理解!!!一遍包过!
    香山杯-2023-Crypto
    node开发MongoDB
    pvrcnn在openpcdet框架下的实现流程
    PCB layout 小功率板子减小干扰方法
    pikachu ssrf
    【视频处理】通过调用图像来重建新影片及计算颜色通道的平均灰度值,并检测帧与前一帧之间的差异(Matlab代码实现)
    KingbaseES V8R3集群维护案例之---pcp_node_refresh应用
    配电房环境智能监控系统:守护电力设施,保障安全运行
    誉天学员笔记:数通HCIE专题之VLAN&交换机高级特性
  • 原文地址:https://blog.csdn.net/middleware2018/article/details/134226418