首页 > 解决方案 > 如何获取与全局键不匹配的 Redis 集合的元素?

问题描述

如何获取没有相应全局哈希的所有键?

我将我的主要数据存储在具有唯一键的哈希中,如下所示:

person_1: name: Anna, gender: female
person_2: name: Mark, gender: male
person_3: name: Pat, gender: female
person_4: name: Jo, gender: female
person_5: name: Robert, gender: male

假设我将所有男性的索引保持为一个 SET,如下所示:

male_persons: 2, 5, 6

如您所见,我偷偷输入了一个“错误”,因为没有 person_6。做一个排序,我可以很容易地建立一个“关系查询”,给我所有的男性:

SORT "male_persons" BY nosort GET # GET "person_*->name"

预期的输出是这样的:

"2", "Mark",
"5", "Robert",
"6", nil

我的问题是,我怎样才能只对没有对应人的 SET 键进行 1 或 2 个查询,即如何在上面的示例中只生成这一行:

"6", nil

甚至更好:

"6"

或者:

person_6

在 SQL 中,这将类似于:

SELECT id FROM male_person_index WHERE male_person_index.id NOT IN (SELECT person.id FROM person);

我知道这样做的一种方法是编写一个 Lua 脚本,但这似乎很基本,应该可以在 1 或 2 个标准查询中执行此操作,而我只是想念它。在这种特殊情况下,性能并不是非常重要的,因此一个接一个地发出 1 个或 2 个甚至 3 个标准查询就足够了。

更新:我写了一个 Lua 脚本的解决方案,但如前所述,我对此并不满意:

eval 'local out = {}; local i = 1; local exists; for _, key in ipairs(redis.call("SMEMBERS", KEYS[1])) do exists = redis.call("EXISTS", "persons_" .. key); if exists == 0 then out[i] = key .. " -> " .. exists; i = i + 1; end; end; return out;' 1 "male_persons"

我不满意的原因是这是“循环”且缓慢并且EXISTS可能会调用数千或数百万次。我仍然认为这应该在 2 或 3 笔交易中完成,而不是 1000 或数百万。

一个优雅的解决方案将采用SORT命令的输出数组STORE,然后以某种方式从该结果中减去另一个查询结果数组,一举减少非孤立键的最终输出,最后给我,仅孤儿键。

那可能吗?

标签: redis

解决方案


您可以尝试以下非常棘手的方法:

溶液

  1. 使用您的排序解决方案并将结果存储到列表中:list1

    SORT "male_persons" BY nosort GET # GET "person_*->name" store list1
    
  2. 按字典顺序(即选项)按person_*->name对male_persons进行排序,并将结果存储到列表中:list2ALPHA

    SORT "male_persons" BY person_*->name ALPHA GET # GET "person_*->name" store list2
    
  3. 删除list1中的所有空字符串,并获取删除的元素数:num

    num = LREM list1 0 ""
    
  4. 从list2中获取第一个num * 2元素:

    LRANGE list2 0 (num * 2 - 1)
    

解释:

  1. 将数据按字典顺序排序,即第2步,我们可以确保不存在的成员在结果列表的前面,即list2,对应的值为空字符串:
127.0.0.1:6379> lrange list2 0 -1
1) "6"
2) ""
3) "2"
4) "Mark"
5) "5"
6) "Robert"
  1. 我们可以从list1中移除所有空字符串,即第 1 步和第 3 步,这样我们就可以得到 hash 中不存在的成员数。

注意:我们不会在步骤 1 中进行排序以节省一些成本。

127.0.0.1:6379> lrem list1 0 ""
(integer) 1
  1. 那么list2num * 2中的第一个元素就是结果,即第4步:
127.0.0.1:6379> lrange list2 0 1
1) "6"
2) ""

推荐阅读