Лучшее решение для поиска пересечения 1 x 1 миллион наборов? Редис, Монго, другие

Привет всем и заранее спасибо. Я новичок в игре NoSQL, но мое нынешнее место работы поставило передо мной задачу сравнивать наборы некоторых больших данных.

В нашей системе есть набор тегов клиентов и целевые наборы тегов. Тег — это 8-значное число.
Набор тегов клиента может содержать до 300 тегов, но в среднем 100 тегов
Целевой набор тегов может содержать до 300 тегов, но в среднем 40 тегов.

Предварительный расчет невозможен, поскольку мы рассчитываем на потенциальную клиентскую базу в миллиард пользователей.

(Эти теги иерархичны, поэтому наличие одного тега означает, что у вас также есть его родительский и предковый теги. Отложите эту информацию на данный момент.)

Когда клиент заходит на наш сайт, нам нужно как можно быстрее сопоставить его набор тегов с миллионом целевых наборов тегов. Набор клиентов должен содержать все элементы целевого набора для соответствия.

Я изучал свои варианты, и пересечение множества в Redis кажется идеальным. Однако мой троллинг в Интернете не показал, сколько оперативной памяти потребуется для хранения одного миллиона наборов тегов. Я понимаю, что пересечение будет молниеносным, но возможно ли это решение с Redis.

Я понимаю, что это грубая сила и неэффективно. Я также хотел использовать этот вопрос как средство, чтобы получить предложения о том, как этот тип проблемы решался в прошлом. Как было сказано ранее, теги хранятся в дереве. Я также начал рассматривать Mongodb как возможное решение.

Спасибо еще раз


person MFD3000    schedule 19.06.2012    source источник
comment
Это типичная дилемма между использованием хранилища/памяти и временем обработки, не так ли? Вы можете рассчитать результирующий набор тегов при обновлении тегов, сохранить его и обслуживать быстрее или выполнить динамический расчет, когда данные действительно нужны. Вы можете рассмотреть возможность выбора первого варианта, если обновления тегов не так распространены, или подумать о варианте кластерной базы данных (например, Clustrix).   -  person Ege Özcan    schedule 19.06.2012
comment
Спасибо. Я должен был уточнить. В настоящее время мы делаем предварительные расчеты, но если мы добьемся успеха как компания, мы можем рассчитывать на миллиард потенциальных клиентов. Я рассмотрю Clusterix   -  person MFD3000    schedule 19.06.2012
comment
MongoDB ничего не предлагает для пересечения множеств. И если у вас есть немного оперативной памяти (например, 100+ ГБ), вы можете хранить довольно много ключей в Redis :)   -  person Sergio Tulentsev    schedule 19.06.2012
comment
как уже упоминалось, у MongoDB нет ничего особенного для быстрого пересечения. Redis имеет хорошую поддержку наборов, но, на самом деле, ничего особенного для быстрых пересечений, таких как пересечение наборов битов и т. Д. Взгляните, например, на Lucene/Solr для быстрых реализаций (которые вы можете использовать в качестве ссылки). С точки зрения памяти: 1 миллион тегов равен 1 миллиону битов + хэш-карта, содержащая 1 миллион тегов один раз. Так что это должно быть выполнимо :). +   -  person Geert-Jan    schedule 19.06.2012
comment
Redis имеет эффективную структуру данных intset, интеллектуальный алгоритм пересечения для нескольких наборов и при необходимости может манипулировать наборами битов с помощью команды BITOP (redis.io/commands/bitop)   -  person Didier Spezia    schedule 20.06.2012
comment
@Дидье спасибо, учусь каждый день   -  person Geert-Jan    schedule 20.06.2012
comment
Спасибо, парни. Все это было действительно интересно. Дал мне несколько хороших мыслей о Redis. Я также придумал некоторую архитектурную структуру для mongoDB, которая может привести меня туда, где мне нужно. Я опубликую это обсуждение переполнения стека, когда доберусь туда   -  person MFD3000    schedule 21.06.2012


Ответы (3)


Это интересная проблема, и я думаю, что Redis может помочь здесь.

Redis может хранить наборы целых чисел, используя оптимизированный формат «intset». Дополнительную информацию см. на странице http://redis.io/topics/memory-optimization.

Я считаю, что правильная структура данных здесь представляет собой набор целевых наборов тегов, а также обратный индекс для сопоставления тегов с их целевыми наборами тегов.

Чтобы сохранить два целевых набора тегов:

 0 -> [ 1 2 3 4 5 6 7 8 ]
 1 -> [ 6 7 8 9 10 ]

Я хотел бы использовать:

 # Targeted tag sets
 sadd tgt:0 1 2 3 4 5 6 7 8
 sadd tgt:1 2 6 7 8 9 10
 # Reverse index
 sadd tag:0 0
 sadd tag:1 0
 sadd tag:2 0 1
 sadd tag:3 0
 sadd tag:4 0
 sadd tag:5 0
 sadd tag:6 0 1
 sadd tag:7 0 1
 sadd tag:8 0 1
 sadd tag:9 1
 sadd tag:10 1

Этот обратный индекс довольно легко поддерживать, когда из системы добавляются/удаляются целевые наборы тегов.

Потребление глобальной памяти зависит от количества тегов, которые являются общими для нескольких целевых наборов тегов. В Redis довольно легко хранить псевдоданные и моделировать потребление памяти. Я сделал это с помощью простого скрипта node.js.

Для 1 миллиона целевых наборов тегов (теги представляют собой 8-значные числа, 40 тегов в наборе) потребление памяти приближается к 4 ГБ, когда целевые наборы тегов используют очень мало общих тегов (более 32 МБ). записей в обратном индексе) и около 500 МБ, если теги часто используются совместно (всего 100 тыс. записей в обратном индексе).

С такой структурой данных поиск целевых наборов тегов, содержащих все теги данного клиента, чрезвычайно эффективен.

1- Get customer tag set (suppose it is 1 2 3 4)
2- SINTER tag:1 tag:2 tag:3 tag:4
   => result is a list of targeted tag sets having all the tags of the customer

Операция пересечения эффективна, поскольку Redis достаточно умен, чтобы упорядочить наборы по количеству элементов и начать с набора с наименьшим количеством элементов.

Теперь я понимаю, что вам нужно реализовать обратную операцию (т. е. найти целевые наборы тегов, все теги которых входят в набор тегов клиента). Обратный индекс все еще может помочь.

Вот пример в уродливом псевдокоде:

1- Get customer tag set (suppose it is 1 2 3 4)
2- SUNIONSTORE tmp tag:1 tag:2 tag:3 tag:4
   => result is a list of targeted tag sets having at least one tag in common with the customer
3- For t in tmp (iterating on the selected targeted tag sets)
      n = SCARD tgt:t (cardinality of the targeted tag sets)
      intersect = SINTER customer tgt:t
      if n == len(intersect), this targeted tag set matches

Таким образом, вам никогда не придется тестировать набор тегов клиента в сравнении с 1 миллионом целевых наборов тегов. Вы можете положиться на обратный индекс, чтобы ограничить область поиска до приемлемого уровня.

person Didier Spezia    schedule 19.06.2012
comment
Кстати, я никогда не комментировал. Потрясающий ответ. Большое спасибо. Я успешно использую это в течение месяца. - person MFD3000; 30.08.2012
comment
Я был заинтересован в нескольких словах о его производительности. Это реальное время? - person Manuel Arwed Schmidt; 28.03.2015
comment
классный ответ! может быть, вы знаете, как помочь и с этим? :) stackoverflow.com/questions/37986935/ - person meso_2600; 23.06.2016

это может быть полезно:

Практический пример: использование Redis intersect на очень больших наборах (120+ миллионов с 120+ миллионов)

http://redis4you.com/articles.php?id=016&name=Case+Study%3A+Использование+Redis+intersect+on+very+large+sets

person Nick    schedule 29.08.2012
comment
ссылка не работает. вот заархивированная версия этой статьи: https://web.archive.org/web/20170226145031/http://redis4you.com/articles.php?id=016&name=Case+Study%3A+Использование+Redis+intersect+on+very+large+sets - person guilemon; 13.10.2020

Предоставленные ответы помогли мне изначально. Однако по мере того, как наша клиентская база росла, я наткнулся на отличный метод, включающий использование строковых битов Redis и битовых операторов для очень быстрого анализа сотен миллионов пользователей.

Проверьте эту статью. Антирез, создатель Redis, также часто ссылается на это.

http://blog.getspool.com/2011/11/29/fast-easy-realtime-metrics-using-redis-bitmaps/

person MFD3000    schedule 20.02.2013