键空间触发器

基于键空间通知执行 JavaScript 函数

键空间触发器允许您注册一个函数,该函数将在数据库中发生事件时执行。大多数事件是由命令调用触发的,但有两个特殊事件可以独立于命令发生

  1. Expired:当一个键从数据库中过期时,会触发此事件。
  2. Evicted:当一个键从数据库中被驱逐时,会触发此事件。

有关支持事件的完整列表,请参阅 Redis 键空间通知页面

要注册键空间触发器,您需要在加载库时使用 redis.registerKeySpaceTrigger API。以下示例演示了如何注册一个数据库触发器,该触发器在每次修改哈希键时添加一个“最后更新”字段

#!js api_version=1.0 name=myFirstLibrary

redis.registerKeySpaceTrigger("consumer", "", function(client, data){
    if (client.call("type", data.key) != "hash") {
        // key is not a has, do not touch it.
        return;
    }
    // get the current time in ms
    var curr_time = client.call("time")[0];
    // set '__last_updated__' with the current time value
    client.call('hset', data.key, '__last_updated__', curr_time);
});

参数描述

  • consumer:消费者名称。
  • prefix :触发器应该在其上触发的键前缀。
  • callback:要调用的回调函数,遵循与 同步和异步调用 相同的规则。回调函数只会在主分片上被调用。

运行示例

127.0.0.1:6379> hset h x 1
(integer) 1
127.0.0.1:6379> hgetall h
1) "x"
2) "1"
3) "__last_updated__"
4) "1658390831"
127.0.0.1:6379> hincrby h x 1
(integer) 2
127.0.0.1:6379> hgetall h
1) "x"
2) "2"
3) "__last_updated__"
4) "1658390910"

传递给消费者回调函数的 data 参数采用以下格式

{
    "event": "<the event name that fired the trigger>",
    "key": "<key name that the event was fired on as String>",
    "key_raw": "<key name that the event was fired on as ArrayBuffer>"
}

注意,只有当键可以被解码为 JS String 时才会给出 key 字段,否则该值为 null

我们可以使用 TFUNCTION LIST 命令显示触发器信息

127.0.0.1:6379> TFUNCTION list vvv
1)  1) "engine"
    2) "js"
    3) "api_version"
    4) "1.0"
    5) "name"
    6) "foo"
    7) "pending_jobs"
    8) (integer) 0
    9) "user"
    10) "default"
    11) "functions"
   12) (empty array)
   13) "keyspace_triggers"
   14) (empty array)
   15) "stream_triggers"
   16) 1)  1) "name"
           2) "consumer"
           3) "num_triggered"
           4) (integer) 2
           5) "num_finished"
           6) (integer) 2
           7) "num_success"
           8) (integer) 1
           9) "num_failed"
          10) (integer) 1
          11) "last_error"
          12) "TypeError: redis.call is not a function"
          13) "last_exection_time"
          14) (integer) 0
          15) "total_exection_time"
          16) (integer) 0
          17) "avg_exection_time"
          18) "0"

触发器保证

如果传递给触发器的回调函数是一个 JS 函数(不是协程),则保证回调函数将与导致触发器的操作一起原子地调用;这意味着所有客户端只有在回调函数完成之后才能看到数据。此外,还保证回调函数的效果将与触发触发器的命令一起,在 multi/exec 块中复制到副本和 AOF。

如果回调函数是一个协程,它将在后台执行,并且没有关于它在哪里或是否执行的保证。这些保证与 同步和异步调用 中描述的相同。

升级

当使用 TFUNCTION LOAD 命令的 REPLACE 选项升级现有触发器代码时,所有触发器参数都可以被修改。

高级用法

对于大多数用例,registerKeySpaceTrigger API 就足够了。但是,在某些情况下,您可能需要对触发器何时触发有更好的保证。让我们看看以下示例

#!js api_version=1.0 name=myFirstLibrary

redis.registerKeySpaceTrigger("consumer", "", function(client, data){
    if (client.call("type", data.key) != "hash") {
        // key is not a has, do not touch it.
        return;
    }
    var name = client.call('hget', data.key, 'name');
    client.call('incr', `name_${name}`);
});

每当哈希键发生变化时,上面的示例将从哈希中读取 name 字段,例如 tom,并将增加 name_tom 键的值。(**注意:此函数在集群上无法正常工作。我们需要在键的名称上使用 {} 来确保我们写入的是位于当前分片上的键。为了简单起见,我们暂时忽略集群问题**)。运行该函数将得到以下结果

127.0.0.1:6379> hset x name tom
(integer) 1
127.0.0.1:6379> hset x name jerry
(integer) 0
127.0.0.1:6379> get name_tom
"1"
127.0.0.1:6379> get name_jerry
"1"

我们可以看到 name_tom 键被增加了 1 次,name_jerry 键被增加了 1 次。如果我们将 hset 调用包装在 multi/exec 中,我们会得到相同的结果吗?

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> hset x name tom
QUEUED
127.0.0.1:6379(TX)> hset x name jerry
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 1
2) (integer) 0
127.0.0.1:6379> get name_tom
(nil)
127.0.0.1:6379> get name_jerry
"2"

到底发生了什么?name_jerry 被增加了 2 次,而 name_tom 根本没有增加。发生这种情况是因为,在 multi/exec 或 Lua 函数的情况下,通知会在事务结束时发出,因此所有客户端都会收到最后一个写入值的通知,即 jerry

为了修复代码并在 multi/exec 上仍然获得预期结果。触发器和函数允许您指定一个可选的回调函数,该函数将在通知发生时(而不是在事务结束时)运行。对这个回调函数的约束是,它只能读取数据而不能执行任何写入操作。新的代码如下

#!js api_version=1.0 name=lib

redis.registerKeySpaceTrigger("consumer", "", function(client, data){
    if (data.name !== undefined) {
        client.call('incr', `name_${data.name}`);
    }
},{
    onTriggerFired: (client, data) => {
        if (client.call("type", data.key) != "hash") {
            // key is not a has, do not touch it.
            return;
        }
        data.name = client.call('hget', data.key, 'name');
    }
});

上面的代码为我们的触发器提供了一个可选的函数参数 onTriggerFired。该函数将在键发生变化后立即触发,并将允许我们读取键的内容。我们将内容添加到 data 参数中,该参数将传递给可以写入数据的实际触发器函数。上面的代码按预期工作

127.0.0.1:6379> multi
OK
127.0.0.1:6379(TX)> hset x name tom
QUEUED
127.0.0.1:6379(TX)> hset x name jerry
QUEUED
127.0.0.1:6379(TX)> exec
1) (integer) 1
2) (integer) 0
127.0.0.1:6379> get name_tom
"1"
127.0.0.1:6379> get name_jerry
"1"
RATE THIS PAGE
Back to top ↑