计算Redis中匹配属性的数量

时间:2011-12-18 14:38:20

标签: redis

我想看看从PostgreSQL到Redis的数据集迁移是否对特定搜索查询产生了积极影响。不幸的是,我真的不知道如何组织键和值。

我想要的是用户能够提供属性列表,并且应用程序按照尚未输入的属性的升序提供项目列表。

例如:

Item #1 { prop1, prop2, prop4, prop7 }     Query: "prop1 prop3 prop4 prop5"
Item #2 { prop7, prop8 }                   Result: Item #3
Item #3 { prop2, prop3, prop5 }                    Item #1
                                                   Item #2

到目前为止我想出了什么:

#!/usr/bin/python
properties = (1, 3, 4, 5)
items = ["Properties:%s:items" % id for property in properties ]
redis.zunionstore("query:related_items", items)
redis.zinterstore("query:result", { "Items:all": 1, "query:related_items": -1 })

这构建了一组与用户输入的Item连接的Property s(全部得分为1)。然后,计算与所有Items的排序集合(其中每个值的分数是Property的数量)的交集。如果查询中提供了Property的所有Item,则权重设置为创建0分。

由于Item的数量约为600.000个条目,因此此查询大约需要4-6秒。有没有更好的方法来实现这一目标?

2 个答案:

答案 0 :(得分:1)

我想你正在寻找一个Python解决方案,但Ruby的Ohm库是我最喜欢的基于Redis的数据库类似物。考虑到Python和Ruby以及Ohm exceptional documentation之间的相似之处,你可能会发现一些灵感。

答案 1 :(得分:1)

编辑:使用评论中所述的真实属性。

我想我做到了(再一次)。我使用了PHPRedis

我也使用了排序集,但我颠倒了你的模式:每个zset代表成分,每个食谱id 是该zset的成员。因此,每个zset都有相同数量的成员,即应用程序中的每个配方。每个食谱使用或不使用成分。这定义了分数。

加载有点贵,但查询是在<3> 下完成的,对于含有12种成分和600.000种配方的样品。 (你肯定会得到很多!)。


载入

的伪代码:

For every ingredient i on the system
   For every recipe j on the system
      If recipe j uses the ingredient i Then
         score = 1
         INCR recipe:j:ing_count //Will help sorting
         RPUSH recipe:j:ing_list i //For listing all ingredients in recipe
      Else
         score = 0
      End If
      ZADD ing:i score j
   End For
End For

代码:

#!/usr/bin/php
<?
### Total of ingredients
define('NUM_OF_ING',12);

### Total of recipes
define('NUM_OF_RECIPES',600000);

$redis = new \Redis();
$redis->connect('localhost');

for ($ing=1; $ing<=NUM_OF_ING; $ing++) {
    for ($recipe=1; $recipe<=NUM_OF_RECIPES; $recipe++) {
        $score = rand() % 2;
        if ($score == 1) {
            $redis->incr("recipe:$recipe:ing_count");
            $redis->rpush("recipe:$recipe:ing_list", $ing);
        }
        $redis->zAdd("ing:$ing", $score, $recipe);
    }
}
echo "Done.\n";
?>

查询

在粘贴PHP代码和测量运行时间之前,让我做一些观察:

排序是根据使用的成分数量(查询中zsets的)完成的。如果两个食谱使用查询中的所有成分,那么抢七是由一个食谱的附加成分数量来完成的。更多成分,更高的位置。

总和由ZINTERSTORE处理。带总和的zset存储在结果

然后SORT命令查看每个配方的计数键,使用此附加约束定制订单。

代码:

#!/usr/bin/php
<?
$redis = new \Redis();
$redis->connect('localhost');

//properties in query
$query = array('ing:2', 'ing:4', 'ing:5');
$weights = array(1, 1, 1);

//intersection
$redis->zInter('result', $query, $weights, 'sum');

//sorting
echo "Result:\n";
var_dump($redis->sort('result', array('by'=>'recipe:*:ing_count', 'sort'=>'desc', 'limit'=>array(0,10))));
echo "End.\n";
?>

输出和运行时间:

niloct@Impulse-Ubuntu:~$ time ./final2.php 
Result:
array(10) {
  [0]=>
  string(4) "5230"
  [1]=>
  string(5) "79549"
  [2]=>
  string(4) "2871"
  [3]=>
  string(3) "336"
  [4]=>
  string(6) "109279"
  [5]=>
  string(4) "5352"
  [6]=>
  string(5) "16868"
  [7]=>
  string(3) "690"
  [8]=>
  string(4) "3174"
  [9]=>
  string(4) "8795"
}
End.

real    0m2.930s
user    0m0.016s
sys 0m0.004s
niloct@Impulse-Ubuntu:~$ redis-cli lrange recipe:5230:ing_list 0 -1
 1) "12"
 2) "11"
 3) "10"
 4) "9"
 5) "8"
 6) "7"
 7) "6"
 8) "5"
 9) "4"
10) "3"
11) "2"
12) "1"

希望有所帮助。

PS:你可以在尝试后发布你的表现指标吗?