Подтвердить что ты не робот

Предоставление элементов фида активности умеренно масштабируемым способом

В приложении, в котором я работаю, есть фид активности, в котором каждый пользователь может видеть активность своих друзей (так же, как Facebook). Я ищу умеренно масштабируемый способ отображения потока активности определенных пользователей на лету. Я говорю "умеренно", потому что я хочу сделать это с помощью только базы данных (Postgresql) и, возможно, memcached. Например, я хочу, чтобы это решение масштабировалось до 200 тыс. Пользователей с 100 друзьями.

В настоящее время существует таблица основных действий, в которой хранится отображаемый html для данной активности (Джим добавил друга, Джордж установил приложение и т.д.). В этой таблице основных действий хранятся исходный пользователь, html и временная метка.

Затем есть отдельная таблица ( "join" ), которая просто удерживает указатель на человека, который должен видеть это действие в своем корте для друзей, и указатель на объект в основной таблице активности.

Итак, если у меня есть 100 друзей, и я делаю 3 действия, тогда таблица соединений будет расти до 300 элементов.

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

Другой вариант - просто сохранить основную таблицу действий и запросить ее, сказав следующее:

select * from activity where source_user in (1, 2, 44, 2423, ... my friend list)

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

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

Большое спасибо!

4b9b3361

Ответ 1

Я склоняюсь к тому, чтобы иметь только таблицу мастер-активности. Если вы пойдете с этим, это то, что я бы рассмотрел при реализации:

  • Вы можете создать несколько таблиц действий и выполнить UNION ALL при извлечении данных из базы данных. Например, сверните их по месяцам - activity_2010_02 и т.д. Просто перейдем к вашему примеру - 200K пользователей x 100 друзей x 3 действия = 60 миллионов строк. Для PostgreSQL это не относится к производительности, но теперь вы можете рассмотреть это исключительно для удобства и в конечном итоге для легкого расширения в будущем.

  • Это имеет тот недостаток, что вы запрашиваете пользователей, которые никогда не могут быть активными, и по мере роста вашего списка друзей этот запрос может замедляться и замедляться.

Собираетесь ли вы отображать весь фид активности, возвращаясь к началу времен? Вы не представили много деталей в исходном вопросе, но я бы рискнул предположить, что вы будете показывать последние 10/20/100 элементов, отсортированных по метке времени. Пара индексов и предложение LIMIT должны быть достаточными, чтобы обеспечить мгновенный отклик (как я только что тестировал на столе с примерно 20 миллионами строк). Он может быть медленнее на занятом сервере, но это то, что должно быть разработано с помощью аппаратных решений и кеширования, Postgres не будет узким местом там.

Даже если вы предоставляете фиды активности, восходящие к рассвету времени, разбивайте выходные данные на страницу! Предложение LIMIT спасет вас там. Если базового запроса с LIMIT на нем недостаточно, или если у ваших пользователей длинный хвост друзей, которые больше не активны, вы можете сначала ограничить поиск до последнего дня/недели/месяца, а затем предоставить список friend id:

select * from activity 
  where ts <= 123456789 
    and source_user in (1, 2, 44, 2423, ... my friend list)

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

Это просто, если я выберу между двумя решениями, которые вы сейчас рассматриваете. Я также посмотрел бы на такие вещи, как:

  • Пересмотр вашей денормализации таблицы. Является ли сохранение предварительно сгенерированного вывода HTML действительно лучшим способом? Будете ли вы лучше ориентироваться на производительность, имея вместо этого таблицу поиска и генерируя шаблонный вывод "на лету"? Предварительно сгенерированный HTML может показаться лучше с самого начала, но учитывать такие вещи, как дисковое хранилище, API, будущие изменения макета и хранение HTML, возможно, не так привлекательны. Таблица поиска может содержать ваши возможные действия - добавлен друг, измененный статус и т.д., И журнал активности будет ссылаться на этот идентификатор и идентификатор друга, если в этот актив участвует другой пользователь.

  • Выполнение предварительного генерации HTML, но не сохранение его в базе данных. Сохраните материал на диске в виде предварительно сгенерированных страниц. Однако это не серебряная пуля и во многом зависит от соотношения между чтением и чтением на вашем сайте. То есть типичная дискуссионная дискуссия на публичном форуме могла иметь десяток сообщений, но их можно было бы просмотреть сотни раз - хороший кандидат на кеширование. Если ваше приложение больше настроено на немедленные обновления статуса, и вам придется регенерировать HTML-страницу и сохранять ее снова на диске после каждых двух просмотров, тогда в этом подходе мало значения.

Надеюсь, что это поможет.