Рассчитать количество подходящих свойств в Redis - PullRequest
4 голосов
/ 18 декабря 2011

Я хотел бы посмотреть, окажет ли миграция набора данных из 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 с (все с оценкой 1), которые связаны с введенными пользователем Property с. После этого вычисляется пересечение с отсортированным набором всех Items (где оценка каждого значения - это число Property с). Веса установлены, чтобы создать счет 0, если все Property с Item предоставлены в запросе.

Поскольку число Item s составляет около 600 000 записей, этот запрос занимает приблизительно 4-6 секунд. Есть ли лучший способ сделать это?

Ответы [ 2 ]

1 голос
/ 14 января 2012

РЕДАКТИРОВАТЬ: Использованные реальные свойства, как указано в комментариях.

Я думаю, что сделал это (еще раз). Я использовал PHPRedis .

Я также использовал отсортированные наборы, но инвертировал вашу схему: каждый zset представляет ингредиент , и каждый id рецепта является членом этого zset. Таким образом, каждый zset имеет одинаковое количество членов , то есть каждый рецепт в приложении. Каждый рецепт использует или не ингредиент. Это определяет счет.

Загрузка несколько дорогая, но запрос выполняется за 3 с , для образца с 12 ингредиентами и 600 000 рецептов. (У вас их наверняка много!).


Загрузка

Псевдо-код:

For every ingredient <b>i</b> on the system
   For every recipe <b>j</b> on the system
      If recipe <b>j</b> uses the ingredient <b>i</b> Then
         <b>score</b> = 1
         <b>INCR</b> <b>recipe:</b>j<b>:ing_count</b> //Will help sorting
         <b>RPUSH</b> <b>recipe:</b>j<b>:ing_list i</b> //For listing all ingredients in recipe
      Else
         <b>score</b> = 0
      End If
      <b>ZADD ing</b>:i <b>score</b> <b>j</b>
   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";
?>

1031 * Запросы * Прежде чем вставить код 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: Можете ли вы опубликовать ваши показатели производительности после попытки этого?

1 голос
/ 12 января 2012

Я думаю, что вы ищете решение Python, но библиотека Ом для Ruby - мой любимый аналог баз данных Redis. Учитывая сходство между Python и Ruby и исключительной документацией Ома , вы можете найти вдохновение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...