Резюме : для примерно 1 миллиона активных пользователей и 150 миллионов сохраненных действий я делаю это простым:
- Используйте реляционную базу данных для хранения уникальных действий (1 запись на действие / «событие, которое произошло»). Сделайте записи максимально компактными. Структурируйте так, чтобы вы могли быстро захватить группу действий по идентификатору активности или с помощью набора идентификаторов друзей с ограничениями по времени.
- Публикуйте идентификаторы активности в Redis всякий раз, когда создается запись активности, добавляя идентификатор в список «потока активности» для каждого пользователя, который является другом / подписчиком, который должен видеть активность.
Запросите Redis, чтобы получить поток активности для любого пользователя, а затем при необходимости получить связанные данные из базы данных. Вернитесь к запросу базы данных по времени, если пользователю нужно просмотреть далеко назад во времени (если вы даже предлагаете это)
Я использую простую старую таблицу MySQL для обработки около 15 миллионов операций.
Это выглядит примерно так:
id
user_id (int)
activity_type (tinyint)
source_id (int)
parent_id (int)
parent_type (tinyint)
time (datetime but a smaller type like int would be better)
activity_type
сообщает мне тип действия, source_id
сообщает мне запись, к которой относится действие. Итак, если тип действия означает «добавленный в избранное», то я знаю, что source_id относится к идентификатору избранной записи.
parent_id
/ parent_type
Полезны для моего приложения - они говорят мне , что деятельность связана с. Если книга была добавлена в избранное, то parent_id / parent_type сообщит мне, что действие относится к книге (типу) с заданным первичным ключом (id).
Я индексирую (user_id, time)
и запрашиваю действия, которые есть user_id IN (...friends...) AND time > some-cutoff-point
. Отказ от идентификатора и выбор другого кластерного индекса может быть хорошей идеей - я не экспериментировал с этим.
Довольно простые вещи, но они работают, они просты, и с ними легко работать, когда ваши потребности меняются. Кроме того, если вы не используете MySQL, вы можете лучше работать с индексами.
Для более быстрого доступа к последним действиям я экспериментировал с Redis . Redis хранит все свои данные в памяти, поэтому вы не можете поместить туда все свои действия, но вы можете сохранить достаточно для большинства часто посещаемых экранов на вашем сайте. Последние 100 для каждого пользователя или что-то в этом роде. С Redis в миксе это может работать так:
- Создайте свою запись активности MySQL
- Для каждого друга пользователя, создавшего действие, поместите идентификатор в их список действий в Redis.
- Обрезать каждый список до последних X элементов
Redis работает быстро и предлагает способ конвейерной передачи команд через одно соединение, поэтому передача активности 1000 друзьям занимает миллисекунды.
Для более подробного объяснения того, о чем я говорю, см. Пример Twitter Redis: http://redis.io/topics/twitter-clone
Обновление от февраля 2011 г. У меня 50 миллионов активных действий, и я ничего не изменил. В том, чтобы делать что-то подобное, хорошо то, что они используют компактные, маленькие строки. Я планирую внести некоторые изменения, которые повлекут за собой гораздо больше действий и больше запросов об этих действиях, и я обязательно буду использовать Redis для ускорения работы. Я использую Redis в других областях, и он действительно хорошо работает для определенных типов проблем.
Обновление июль 2014 г. У нас около 700 тыс. Активных пользователей в месяц. Последние пару лет я использую Redis (как описано в маркированном списке) для хранения последних 1000 идентификаторов действий для каждого пользователя. Обычно в системе около 100 миллионов записей активности, они по-прежнему хранятся в MySQL и имеют тот же формат. Эти записи позволяют нам обойтись меньшим объемом памяти Redis, они служат для записи данных об активности, и мы используем их, если пользователям нужно перейти на более раннюю страницу во времени, чтобы что-то найти.
Это не было умным или особенно интересным решением, но оно сослужило мне хорошую службу.