33
Как мы храним 75 млн пользователей (пишем неблокируемый сервер) Бирюков Денис Компания Каванга

Как мы храним 75 млн пользователей (Денис Бирюков)

  • Upload
    ontico

  • View
    3.023

  • Download
    0

Embed Size (px)

DESCRIPTION

 

Citation preview

Page 1: Как мы храним 75 млн пользователей  (Денис Бирюков)

Как мы храним 75 млн пользователей (пишем

неблокируемый сервер)

Бирюков ДенисКомпания Каванга

Page 2: Как мы храним 75 млн пользователей  (Денис Бирюков)

Самопиар :)• Рекламная сеть (много баннеров, много сайтов)

• При показе баннера мы используем таргетинги

– уникальные ограничения

– ретаргетинг

– таргетинг по соцдему

• Нужен сервер

– Помним кому, где и что показывали

– Знаем пол и возраст

– Решаем что именно показать

Page 3: Как мы храним 75 млн пользователей  (Денис Бирюков)

Что было?

• - Блокировки

• - Фрагментация

• - Низкий КПД по памяти (в 3,5 раза) удаление объектов раз в час

• + 5% CPU

• + Высокая скорость ответа

• + Сохранение на диск без доп. памяти

• + Быстрый запуск

Многопоточный сервер

Page 4: Как мы храним 75 млн пользователей  (Денис Бирюков)

Задача• 75 млн пользователей за 3 МЕС, в день

меняется 250 млн объектов их описывающих (~ 60 ГБ памяти)

• До 3000 запросов/сек на обновление данных и столько же на их выборку

• Время ответа критично (<= 200 микросек)• Сервис должен быть масштабируемым

Page 5: Как мы храним 75 млн пользователей  (Денис Бирюков)

Задача• Периодически нужно делать бекап базы

из памяти на диск– быстрый подъем при сбое– анализ данных по файлам без опроса

• Сервис должен быть легко расширяемым по функционалу

• сервис должен быть масштабируемым

Page 6: Как мы храним 75 млн пользователей  (Денис Бирюков)

Как можно хранить данные• Memcached?

– Насколько гибка логика удаления? +– Бэкап базы из памяти на диск? +– Как обновлять по UDP (различные

клиенты)? +/-– Кто реализует дополнительную логику

(ретаргетинг)? -

Page 7: Как мы храним 75 млн пользователей  (Денис Бирюков)

В каком виде хранить?• user1

– лог1– лог2

• userN– логM– логM+1

• user1– struct1– struct2

• userN– structM– structM+1

Page 8: Как мы храним 75 млн пользователей  (Денис Бирюков)

В каком виде хранить?• + гибкость• - много памяти• - есть доп.

обработка

• - гибкость• + меньше памяти• + нет доп.

обработки• нужна ↑ производительность

• требования меняются редко

• бонус: sizeof(struct1) = const

Доводы:

Page 9: Как мы храним 75 млн пользователей  (Денис Бирюков)

Резюмируем требования• Время ответа (<= 200 микросек)• Всего храним 67*2 млн юзеров, в день

меняется 250 млн объектов их описывающих (~ 67 ГБ памяти)

• До 3000 з/сек на обновление данных и столько же на их выборку

• Бекап базы из памяти на диск

Page 10: Как мы храним 75 млн пользователей  (Денис Бирюков)

• Однопоточный– нет блокировок

• Неблокируемый ВВ– poll, epoll, kqueue

• Постоянные соединения

• UDP и TCP клиенты

• Свои аллокаторы (new/delete)

• Свои определения 'самый старый' объект

• Простая логика сохранения (fork)

Архитектура

Page 11: Как мы храним 75 млн пользователей  (Денис Бирюков)

• Все сокеты неблок: – fcntl(sock, F_SETFL, O_NONBLOCK);

• Неблокируемый ВВ (мультиплексирование)– poll, epoll, kqueue

• Постоянные соединения

• UDP и TCP клиенты

Сетевой ВВ

Page 12: Как мы храним 75 млн пользователей  (Денис Бирюков)

Протокол• TCP, UDP:

– read(...) - заголовок– readv(...) - данные– Action(...) - обработка

• TCP:– writev(...) - заголовок,

данные

struct MSG_TAB { int32_t size; int32_t func;};struct MSG_DATA { MSG_TAB h; char data[h.size];};

Page 13: Как мы храним 75 млн пользователей  (Денис Бирюков)

ОбъектыUser: 128 bytesint64_t user_id;int64_t time_create;char mask[32];....

IndexHolder flights, banners;

IndexHolder places, auditory;

ParticularRestrictions:идентификаторотгрузкапоказкликсобытие...

Retarget:идентификатор....

Page 14: Как мы храним 75 млн пользователей  (Денис Бирюков)

Объекты, «устаревание»• User — есть массив памяти под объекты

– старый тот кто дольше всех не появлялся в сети (+ память на 2-х связный список)

• IndexHolder (ArrayList) — есть массив объектов

– + 2-х связный список — есть избыток

• ParticularRestrictions (Flight, Banner), Retarget (Place, Auditory) - циклический массив.

– самый старый по времени создания

Page 15: Как мы храним 75 млн пользователей  (Денис Бирюков)

Как храним user?• User хранятся std::map<u_int64_t, User*>

– map — не самый быстрый контейнер, но зато легко выделять память для него

– для убыстрения работы создадим 64 std::map

• Память под std::map выделятся блоками по мере необходимости. Есть небольшой массив — хранит ссылки на свободные участки памяти

Page 16: Как мы храним 75 млн пользователей  (Денис Бирюков)

Сохранение на диск• По специальной UDP датаграме делаем fork

• Дочерний процесс читает и записывает на диск 4 куска памяти: User, ParticularRestrictions, Retarget, IndexHolder. Целостность данных обеспеченна.

• Результаты (0,75+4+1)/(9,375) ~ 61% КПД (> в 1,6 раза):

– User — 2,125 Gb (1,5 Gb (main) + 0,625 Gb (add))

– IndexHolder — 1,5 Gb (1 GB (main) + 0,5 Gb (add))

– ParticularRestrictions — 4 Gb

– Retarget — 1 Gb

– std::map — 0,75 Gb

Page 17: Как мы храним 75 млн пользователей  (Денис Бирюков)

Емкость памяти• По прикидкам скорость просмотра: 1 ~ 1,5 баннера / 1 мес.

• Время жизни всех объектов ~ 1 мес.

• Потеря активного flight / banner, менее критична чем потеря профиля пользователя.

• Если потеряем старые auditory (place), то охват уменьшится, но качество повысится.

– User — 67 108 864; std::map — сколько нужно

– ParticularRestrictions — 134 216 960

– Retarget — 134 217 728

– IndexHolder — 268 435 456 > 134 216 960 + 134 217 728 !

Page 18: Как мы храним 75 млн пользователей  (Денис Бирюков)

Логика. Action().• switch(readheader.func) {

– case (SET_USER_EVT): юзер сделал хит

– сase (SET_USER_MASK): юзер изменил профиль

– сase (SET_USER_ADT): юзер добавлен в аудиторию

– сase (SET_RETARGETS): обновили ретаргетинги

– сase (SET_CAMPAIGN): обновили уникальность по РК

– сase (GET_USER_EXP): описание юзера, выдаем банер

– сase (GET_USER_EVT): описание юзера, проверяем клик

– сase (UU_CONTROL): служ: fork, fork_info, mem_info

Page 19: Как мы храним 75 млн пользователей  (Денис Бирюков)

Архитектура

uuserver1

uuserver2

uuserver3

uuserver4

uuserver1

uuserver2

uuserver3

uuserver4

front1

front2

Page 20: Как мы храним 75 млн пользователей  (Денис Бирюков)

Кол-во запросов в сек от Exp

Page 21: Как мы храним 75 млн пользователей  (Денис Бирюков)

Время ответа для Exp

Page 22: Как мы храним 75 млн пользователей  (Денис Бирюков)

Время ответа для Exp

Page 23: Как мы храним 75 млн пользователей  (Денис Бирюков)

Кол-во запросов в сек от Evt

Page 24: Как мы храним 75 млн пользователей  (Денис Бирюков)

Время ответа для Evt

Page 25: Как мы храним 75 млн пользователей  (Денис Бирюков)

Время ответа для Evt

Page 26: Как мы храним 75 млн пользователей  (Денис Бирюков)

Кол-во обновлений

Page 27: Как мы храним 75 млн пользователей  (Денис Бирюков)

Время для обновлений

Page 28: Как мы храним 75 млн пользователей  (Денис Бирюков)

Кол-во добавлений в аудит.

Page 29: Как мы храним 75 млн пользователей  (Денис Бирюков)

Время добавлений в аудит.

Page 30: Как мы храним 75 млн пользователей  (Денис Бирюков)

Резюме по боевой нагрузке• Кол-во TCP запросов: 37 тыс + 2,6 тыс ~ 39,6 тыс

з/мин

• Кол-во UDP пакетов: 38,3 тыс з/мин

• Кол-во аудиторных UDP пакетов: 4 тыс з/мин.

Итого на сервер сейчас в номинальном режиме:~ TCP 670 з/сек.~ UPD 680 з/сек.А каков предел по нагрузке?

Page 31: Как мы храним 75 млн пользователей  (Денис Бирюков)

0,5 млн 1 млн 1,5 млн 2 млн0

10

20

30

40

50

60

70

80

50 потоков100 поток180 потоков

Тестовая нагрузка AMD Opteron 2800.13-MHz 2*4, 16 Gb (memory)

Количество запросов

Вре

мя

тест

а, с

ек

Page 32: Как мы храним 75 млн пользователей  (Денис Бирюков)

Тестовая нагрузка

количество запросов в сек0

10000

20000

30000

40000

50000

60000

70000

80000

90000

50 потоков100 потоков180 потоков

Кол

иче

ство

зап

росо

в в

сек

Page 33: Как мы храним 75 млн пользователей  (Денис Бирюков)

Вопросы?