如何在Redis中通过另一个SET的连接值来过滤任何SET。

在Redis中,我有一个过滤器优化问题。

我有一个Redis SET,它在语料库中保存了一种类型的文档和位置对。

例如:

smembers type_in_docs.1

结果是文档。pos pairs

array (size=216627)
0 => string '2805.2339' (length=9)
1 => string '2410.14208' (length=10)
2 => string '3516.1810' (length=9)
...

我创建的另一个Redis集合根据用户选择实时。它包含所选的文档。

smembers filteredDocs

我想根据用户文档id选择来过滤doc.pos对**"type_in_docs"集。实际上,如果我没有在set中使用拼接值,那么使用SINTER**很容易。

因此,我按照以下方式实现了php filter代码。它可以工作,但需要优化。在大的doc.pairs set中需要太多时间。 (接近15万名成员后!)

$concordance= $this->redis->smembers('types_in_docs.'.$typeID);
$filteredDocs= $this->redis->smembers('filteredDocs');

$filtered = array_filter($concordance, function($pairs) use ($filteredDocs) {
    if( in_array(substr($pairs, 0, strpos($pairs, '.')), $filteredDocs) ) return true;
   });

我尝试了有分数的排序集合作为docId。但是找不到得分值的intersect或filter选项。

我正在思考并寻找基于支持的键,集合或Lua脚本的Redis解决方案以进行时间优化。但是没有找到。

如何使用拼接值过滤Redis集?

感谢帮助。

点赞
用户3160475
用户3160475

你的代码很慢,主要原因是从Redis传输了大量数据到PHP过滤器。一般来说,应该尽可能在服务器上执行尽可能多的筛选操作。为此,您需要在CPU和RAM之间做出某种代价。

有很多方法可以做到这一点,这里介绍一种:

  1. 确保您使用的是Redis v2.8.9或更高版本。

  2. 为了有效地查找doc,保持您的doc.pos对不变,但使用分数=0的有序集合,例如:

    ZADD type_in_docs.1 0 2805.2339 0 2410.14208 0 3516.1810
    

这将允许你模仿SISMEMBER来查找集合中的doc

    ZRANGEBYLEX type_in_docs.1 [<$typeID> (<$typeID + "\xff">
  1. 现在,您只需在通常较小的filterDocs集合上执行SMEMBERS,然后调用每个ZRANGEBYLEX即可获得立即收益。

  2. 如果您想做得更好——在极端情况下(例如大的filterDocs,小的type_in_docs),应采取相反的策略。

  3. 如果您想做得更好,使用Lua来封装过滤逻辑,例如:

    -- @usage:  redis-cli --filter_doc_pos.lua <filter set keyname> <type pairs keyname>
    -- @returns: list of matching doc.pos pairs
    
    local r = {}
    
    for _, fv in pairs(redis.call("SMEMBERS", KEYS[1])) do
        local t = redis.call("ZRANGEBYLEX", KEYS[2], "[" .. fv , "(" .. fv .. "\xff")
        for _, tv in pairs(t) do
            r[#r+1] = tv
        end
    end
    
    return r
    
2015-02-05 14:41:06