Архитектура Ленты на Одноклассниках

Post on 10-May-2015

4.923 Views

Category:

Technology

3 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Архитектура Ленты на Одноклассниках

Алина Васильева

разработчик сервиса ЛентаОдноклассники

alina.vasiljeva@odnoklassniki.ru

2

Одноклассники

• Социальный портал• Граф друзей

• Аудитория:– 250 млн аккаунтов– 6 млн пользователей онлайн– более 40 млн посетителей в день

в среднем 90 друзей12 групп

3

Лента

4

Лента

• Один из ключевых сервисов портала

• Цели– показ пользователю действий и событий друзей и групп– распространение контента по порталу– стимулирование пользователей создавать контент

• Задачи– показ интереснейшего контента максимально большому

количеству пользователей– агрегация– группировка– сортировка– выживание в условиях высокой нагрузки

5

События

• В ленту попадают события более чем 100 разных типов– Фото– Статусы– Дружбы– Классы– Подарки– Игры– Группы– Музыка– Видео– и многое другое...

Больше всего добавляется классов к фото

По показам лидируют темы из групп

6

Запись и хранение событий

• Для достижения цели необходимо записывать и хранить события каждого пользователя

• Главная сущность: Feed

• Для каждого пользователя нужно хранить List<Feed>

class Feed {

long createDate;

short feedTypeId;

Long referenceId;

...}

7

Запись и хранение событий

• SQL ?

• SELECT, JOIN, COUNT...

8

Нагрузка

• В час пик пользователи генерируют 1 миллион событий в 5 минут (>3000 операций записи в секунду)

• Запросы ленты конкретного пользователя: >4000 в сек• Запросы общей ленты на главной: >9000 в сек

9

История развития хранилища фидов

• SQL решение не рассматривалось изначально

– высокая нагрузка

– необходима высокая доступность

– характер данных, запросов и нагрузки

10

История развития хранилища фидов

• Oracle Berkeley DB

– Key / Value хранилище CP-типа– Key: long feedOwnerId– Value: List<Feed> byte[]– Хранятся последние N записей (500)

• Через ~2 года добавили Voldemort + Tarantool

– Key / Value хранилище AP-типа– Хранение данных в памяти– Более высокая производительность– Более высокая доступность– Хранятся последние 30 записей– Использовался одновременно с Berkeley

11

Переход на Cassandra

• Причины– Нестабильность Berkeley– Ограничение на объём хранимых данных– Упирались в трафик

• необходимо пересылать по сети все данные

• Преимущества– Высокая доступность, распределённость– Масштабирование, восстановление на ходу– Высокая скорость записи– Скорость чтения не зависит от объема– Возможность реализации бизнес-логики

12

Cassandra

• Хранение данных в Column Family– Row Key– Column Key + Column Value + Timestamp

13

Хранение фидов в Cassandra

14

Общая картина

Feed Proxy - координирующее Java-приложение

15

Инфраструктура ленты

• Сервера распределены по трём дата-центрам

Feed Proxy кластер:21 000 запросов/сек

Feed Storage кластер:120 000 запросов/сек

16

Инфраструктура ленты

• Выдерживаем потёрю целого дата-центра

Приложенияобновляютсяпутём отключениясерверов в одном ДЦ

Обновлениекластера Proxy:5 минут

Обновлениекластера Storage:30 минут

17

Общая лента

• Задача – показ ленты событий от всех друзей

распространениеинформации

получениеинформации

18

Общая лента

19

Список подписок

• При дружбе пользователи добавляются друг к другу в список подписок (ObservedList)

• Формируется список друзей, за событиями которых пользователь следит и которые попадают к нему в ленту

• Храним список подписок для каждого пользователя

class ObservedList {

List<ObservedItem> items;

...

}

class ObservedItem {

long feedOwnerId;

long lastUserAccessTime; long lastFeedOccurrenceTime;

...

}

20

Хранение списка подписок

• SQL снова не подходит– 350 добавлений в секунду (18 млн за сутки)– 9000 чтений в секунду– характер данных и запросов

• Хранили в Berkeley DB, перешли на Cassandra

21

1. Получаем список подписок из ObservedList

2. По каждой подписке получаем список фидов из Storage

3. Объединяем, сортируем

Алгоритм сборки общей ленты [simplified]

List<Feed> feeds = new ArrayList<Feed>();

ObservedList observedList = getObservedList(feedFollowerId);

for (ObservedItem item: observedList.getItems()){

List<Feed> userFeeds =

getFeeds(observedItem.getFeedOwnerId());

feeds.addAll(userFeeds);}

Collections.sort(feeds, FEEDS_COMPARATOR);

Выполняетсяна Feed Proxy

22

И опять нагрузка

• 9000 запросов получения общей ленты в секунду– даже с учётом кэширования на вебе– помним про 90 друзей и 12 групп у пользователей!– 9000 * 102 = 918 000 походов в базу в секунду

• Базы данных не в состоянии эффективно обработать такое количество запросов

Нужен кеш событий общих лент пользователей

23

Feed Cache

• Java-приложение, цель которого получение, хранение и отдача общих лент

24

Масштабы Feed Cache

• Самое мощное приложение инфраструктуры ленты

• 64 сервера, распределённые по трём дата-центрам

• Кол-во запросов: 100 тысяч в секунду (~1500 на сервер)

• В кэши входит 100 млн событий в 5 минут

• Трафик: 1 Гб/сек– 10 Мб/сек входящего на 1 сервер– 6 Мб/сек исходящего на 1 сервер

• Объем хранимых данных: 6 ТБ (в RAM)– ~100 ГБ на 1 сервере– в среднем 100 KБ на пользователя

25

Хранение данных в Feed Cache

• По ключу идентификатора пользователя хранится список фидов

• Длина списка ограничена– 1000 записей, но, бывает, уменьшаем (при повышенной нагрузке)– специальный алгоритм по вытестению данных из кэша– планируем переход на Cassandra

26

Хранение данных в Feed Cache

• Помимо списка фидов хранятся также дополнительные данные– время последнего логина– время последнего открытия ленты– время последнего обновления данных с Feed Proxy– значение последнего выбранного фильтра

• Данные сериализуются и хранятся в Off-Heap памяти

• Раз в 12 часов сервер записывает данные на диск (снепшот)

• При рестарте приложение стартует со снепшота ~8 минут

• При старте без снепшота есть механизмы обеспечивающие плавную загрузку данных

27

Кластер Feed Cache

• Шардинг – каждый сервер обслуживает 1/64 часть пользователей– партиционирование по id пользователя

• У каждого сервера есть «заместитель», на который перенаправляются запросы в случае недоступности

• Обновление приложений всего кластера занимает ~1 час (обновление по ¼ серверов)

28

Feed Cache - Отдача данных

• Feed Cache запрашивает новые события с Feed Proxy по истечению временного периода (15 минут) – инфраструктура уже выдерживает обновления раз в 1 минуту

29

Время последнего события

• И всё же нагрузка на хранилище фидов получается черезмерно большая

• При каждом обновлении на Feed Proxy обходить базы всех друзей неоправданно дорого• ведь новые события могли появиться всего у пары из них, а то

вообще ни у кого

• Решение - отдельно хранить дату добавления последнего события в ленту пользователя

• Отдельная база: Voldemort + Tarantool (key / value)– long feed_owner_id long last_create_date

30

База UpdateInfo

• При добавлении нового события обновляется timestamp в базе UpdateInfo

• Далее это значение проверяется при сборке событий для общей ленты

31

• Перед запросом в базу Feeds проверяется значение UpdateInfo - появились ли новые события

Алгоритм сборки общей ленты [improved]

public List<Feed> collectRecentFeeds(long feedFollowerId, long

lastUpdated){

1. ПОЛУЧАЕМ СПИСОК ПОДПИСОК (OBSERVED LIST)2. ДЛЯ КАЖДОЙ ПОДПИСКИ

3. ПРОВЕРЯЕМ UpdateInfoif (hasNewFeeds(item.getId(),

lastUpdated)){4. ПОЛУЧАЕМ ФИДЫ5. ДОБАВЛЯЕМ ФИДЫ В ОБЩИЙ

СПИСОК}

}...

}Хотя в UpdateInfo тоже ходим не каждый раз.

Значения кэшируются в ObservedList.

32

Группировка событий

• Проблема – повторяющиеся бесполезные события

Группировка событий

• Решение – группировка событий

33

• Исключение событий

34

Группировка событий

• Решение – инфраструктура мержеров событий

List<Feed> feeds = getFeeds();for (Merger merger: mergers){

merger.mergeFeeds(feeds);}

Длина спискауменьшаетсяна ~25%

35

Приоритеты событий

• Проблема – событий много, нужно показать пользователю самое интересное

• Решение – подсчёт весов событий и пересортировка на их основании

• Вес подсчитывается при входе события в Feed Cache

• Также анализируется состояние кэша– подсчитывется сколько событий каких типов уже присутствует в кэше

множество дополнительных коэффициентов и параметров

36

Feed Stats

• Отдельное Java-приложение с хранением данных в Cassandra

• Сбор статистики о предпочтениях пользователей

• Записывает действия, которые пользователь совершает по отношению к своим друзьям– 53 000 записей в секунду

• На основании собранной статистики подсчитывает веса друзей

• Далее этот вес используется лентой при подсчёте веса события

37

Полная картина

38

Real-time доставка событий

• Проблема: чтобы увидеть новые события пользователю необходимо обновить страницу

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

• Задача: доставлять события в ленты пользователей моментально и автоматически

39

Real-time доставка событий

После добавления нового события Feed Storage нотифицирует Feed Cache друзей в онлайне, отсылая им фид, который далее переправляется прямо на Web

Спасибо!

Алина Васильева

разработчик сервиса ЛентаОдноклассники

alina.vasiljeva@odnoklassniki.ruodnoklassniki.ru/alina

v.ok.rujob@odnoklassniki.ru

top related