?

Log in

Previous Entry

Самая свежая тема для вопросов по мастер-классам #highload (http://php.spb.ru).

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

Во время мастер-класса можно задавать вопросы в чат телеграма https://telegram.me/phpspbru

-------------------------------------------------------------------------

Ближайший мастер-класс #Highload в 2016 году:

- 18 июня 2016 в Москве, билеты и запись: http://devconf.ru

- 26 июня 2016 в Санкт-Петербурге, запись: http://php.spb.ru/ms/sign-up.html

-------------------------------------------------------------------------

Comments

( 17 comments — Leave a comment )
(Anonymous)
Jun. 22nd, 2016 04:15 pm (UTC)
5 вопросов которые возникли первым делом
Доброго времени суток, Дмитрий. Хотелось бы сказать большое спасибо за интересную презентацию.
Вопросы которые появились после пары дней обдумывания:

1. Имеет ли смысл начать использовать Postgre для поиска, если на данный момент используем Mysql & Sphinx?

2. Есть ли возможность возникновения ситуации, когда для одного типа очереди используется более 1 инстанса редиса? Если да, то как правильно это разгребать (как драйвер узнает куда отправить очередной воркер?)?

3.Представим что в очередь последовательно попало 2 задачи на апдейт одних и тех же данных. 2 воркера забрали задачи. И, по закону Мерфи, задача 2 выполнилась быстрее чем задача 1, и в базе оказались неверные данные. Ситуация из головы, но хотелось бы узнать, возможно ли такое, и если да, то как бороться?

4.Не совсем понятно, как больше 1 воркера будут работать с очередью? Ведь команды должны выполняться последовательно...
Предположим есть n команд в очереди которые хотят писать в Mysql. Как понять что предыдущая выполнилась корректно и можно выполнять следующую?
И если вдруг произошла ошибка, я помню вы говорили что ставите команду в очередь, но ведь нельзя ставить её в конец очереди, она должна выполниться перед всеми остальными.

Пролейте чуть больше света на очереди и их реализацию =)

5. Сейчас мы пытаемся спроектировать систему по вашим рекомендациям.
На данный момент для роутинга по серверам получилось 2 файла, первый это карта спотов, второй описывает где лежат все остальные инстансы будь то mysql_search для поиска или redis_queue для очередей итд..
Написали драйвер который с этим делом работает. На данный момент думаем, как правильно организовать очереди. Что еще желательно предусмотреть в процессе написания проекта?
Пока есть время имеет ли смысл сразу заложить логику авторайзеров, превьюшек итд или это преждевременная оптимизация?
spb_borodin
Jun. 22nd, 2016 04:43 pm (UTC)
Re: 5 вопросов которые возникли первым делом
1. Да, если более 3х серверов отведено целиком на поиск. И только, если планируемая выгода больше, чем потраченная зп программистами на переделку. Но если все и так хорошо, то не трогайте, до большого рефакторинга .-)

2. Да. По user_id или еще какому-то аргументу из каждого сообщения, выбирается нужная очередь. Например, que_profile_1, она насчитана для первого миллиона юзеров.

3. Не значительная проблема. Юзер запросил изменения дважды слишком быстро? Вот они и выполнились. При необходимости в базе пишем время создания заявки, игнорируя более ранние. Или до помещения в очередь генерим из сиквенса порядковый номер, тоже игнорируем более ранние. Сами сообщения можно держать в таблице, раз так важен порядок.

4. Нет, не должны. Иначе, см. выше. Вы уже говорите о задаче, где нужно планово обработать большое число заявок. Это не те очереди для отложенного исполнения нагрузки от задач. Нужен четкий порядок - таблица в базе, попытки выполнить, статусы всевозможные и т.д. Да, такие задачи есть, но их мало. Обычно, все реализуется так, что порядок исполнения задач из очереди не важен.

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

spb_borodin
Jun. 23rd, 2016 07:36 am (UTC)
Re: 5 вопросов которые возникли первым делом
Обдумал еще раз вопрос. Предположим, нет у нас очередей и нагрузка выполнятся напрямую в пхп или другой язык. От браузера пришло 2 команды, одна за другой, с паузой, например в 3 секунды. Что мешает системе запустить первый поток для обработки и заморозить его на 10 секунд? При недостатке CPU так и происходит с воркерами - они подвисают, ибо тики процессора не резиновые. Так вот, первый поток висит, потом запустился второй и обработал целиком раньше, чем первый. Т.е. проблема не в очередях вовсе, а просто в особенностях многопоточности.

Бояться этого не надо. Метод, который исполняет команду из очереди должен быть написал качественно. Чтобы другие потоки при параллельном обращении не смогли что-то сломать.

А чтобы не было ситуации, что более поздний запрос отработает раньше, помимо всяких блокировок и т.д., в объект очереди нужно включать какой-то маркер. Например, текущую версию данных из базы, копию какого-то поля. Тогда, помимо блокировок/транзакций, в начале обработки проверяем данное поле на соответствие текущему в базе. Это напоминает логику CAS.

В общем, вы как программист, уже дальше додумайте это. Либо проверять на чтение некое поле, либо завести внешнюю переменную, либо полноценная SQL таблица со статусами и порядком, как я в предыдущем сообщении написал.
(Anonymous)
Jun. 23rd, 2016 03:38 pm (UTC)
Много размышлений и текста)
Дмитрий здравствуйте. У нас после многих обсуждений получился промежуточный результат и мы бы хотели чтобы вы указали на ошибки и куда двигаться дальше.
Как по нашему мнению это будет работать на старте проекта(проект - близкий аналог авито, главные сущности пользователи и товары):

1.Основная сущность - пользователь. Споты мы разбиваем по id пользователя. Размер спота мы взяли 10 000 т.к. mysqldump 1000 юзеров занимает намного меньше 100мб и не очень полезно для файловой системы. user_id и остальные автоинкременты генерируем из redis при помощи Incr.
2.Карту спотов храним в Mysql. Пишем скрипт для создания нового спота.
3.На "главном" сервере Mysql база для поиска по товарам.
4.Далее идет система очередей. Строим по принципу 1 очередь обрабатывает запросы от n пользователей. Т.к. пока нет опыта, не знаем и каким количеством юзеров ограничить очередь.
5.Работаем с авторайзером по вашей схеме, читаем и пишем туда в первую очередь, далее суем в очередь.
6.В идеале, если успеем, сделать прослойку для главной страницы сайта.
7.Уже после запуска думать об оптимизации, кеше, скриптах которые облегчат жизнь и тому подобных вещах если останется время и возникнет потребность.

Вопросы к вам:

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

2. Сколько юзеров оптимально держать в авторайзере? По нашим прикидкам получается не менее 100 000 на один авторайзер.
В качестве избавления от "засорения" памяти в авторайзере мы думаем класть туда данные с TTL 1 неделю. Имеет ли смысл так делать или лучше всегда хранить даже "трупы"?

3. Бизнес-логика и работа с деньгами. Я помню вы говорили что в этих местах юзер может подождать и это логично :) То есть я не ошибусь, если скажу что здесь мы пишем сразу в авторайзер и тут же без очереди в Mysql?
При такой схеме, где вы храните данные о финансовых операциях? Отдельные базы под это или организовано как справочник?

4. У нас в системе есть категории и гео. При такой структуре мы не можем решить, есть ли смысл хранить такую информацию в каждом споте или положить их грубо говоря на главный сервер?
Больше склоняемся к варианту положить на "главный сервер" и положить в быструю память каждого сервера (это дело весит менее 100 мб).

5. По поводу разбора очереди возникла такая задумка:
Если по каким-то причинам воркеру не удалось выполнить sql команду, он кладет её во вторую очередь более приоритетных команд которая у каждого воркера своя.
Таким образом алгоритм воркера - проверить приоритетную очередь команд, если там пусто, забрать из основной. Это даст нам возможность более или менее соблюдать порядок в котором команды пришли.
Правда очень смахивает на велосипед из презентации, поэтому и решили спросить, есть ли смысл :)

6. На засыпку) просто интересно, как вы храните логи проекта и как много сущностей у вас имеют возможность писать в логи.
spb_borodin
Jun. 23rd, 2016 09:44 pm (UTC)
Re: Много размышлений и текста)
по первой части

1. Если ваш юзер - это просто инфо о регистрации, а не жирный юзер, как в соц.сетях, то нормально. Если юзер - это одно объявление, которое действительно небольшое по объему - 10 000 для спота нормально.

2. Да, в базе. А копию ее поместите рядом с пхп. Либо в файлы, которые будете инклюдить, либо в локальный для пхп редис/мемкеш.

4. Одна очередь - один тип запросов/команд/операций. Старайтесь делать как можно больше очередей. Тогда в случае проблем, будет очевидно по длине очередей, где что не так.

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


spb_borodin
Jun. 23rd, 2016 09:44 pm (UTC)
Re: Много размышлений и текста)
по второй части пунктов

1. Да любое кол-во юзеров в одну очередь. Главное (как и выше пункт 4): 1 очередь = 1 тип сообщений. Не мешайте их в кучу. Дополнительно, можно поделить одну очередь на диапазоны юзеров. Но не нужно, это преждевременная оптизация. Пока у вас в проекте менее 20 серверов, не занимайтесь делением одной операции на N очередей. Еще раз см. про главное.

2. Да, трупы лучше не держать, если точно известно, что их никто не будет читать. Правда, есть некоторый минус. Если вы захотите перебрать всех юзеров в быстром итераторе, то уже не сможете это сделать. Часть юзеров будет мгновенно читаться с авторайзера, другой большей части там не будет, что вызовет SQL нагрузку на диски, т.е. большие тормоза.

Экспериментируйте как хотите с авторайзерами. В любой момент времени, если схема ключей в автозайзерах не будет устраивать, это легко переразбить. Это же не SQL. Без остановки проекта наполняете рядом еще N новый редисов с иной схемой, повторно копируя только что изменившиеся данные, а потом переключаете трафик на новые авторайзеры, в одно мгновение. Учтите, что большое число коннектов к одному редису может вызывать ошибку: либо таймаут соединения, либо в уже состоявшемся соединении обычный get вернет false, означающий вовсе не отсутствие переменной, а проблему сети. В таких случаях нужно убить коннект, повторить его, повторить команду. И редис уже ответит. Проверяйте код возврата последней команды.

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

Так же по каждой операции ведите лог: запрос от юзера на зачисление денег, попытка перевода денег в платежной системе, запись денег на локальный счет, оказание услуги, списание денег за услугу. Сначала пишите операцию, которую собираетесь сделать, потом выполняете операци. Когда пхп упадет в произвольном месте и юзер пожалуется на пропажу денег, то будет очевидно, как восстановить. По деньгам никогда никаких проблем не будет из-за синхронной записи ее на диск в танзакции и через репликацию в копию. Лог можно хранить как на биллинг сервере (надежно, медленно), так и в обычных спотовых таблицах (нормально).

4. Оцените, как часто читают эту информацию. Если при каждом хите - то делайте локальную копию данных (файлы, ОЗУ) на каждой тачке с пхп. Например, так делают с картой спотов, ибо ее читают при каждом хите, и не раз. Возможно, гео инфо читают на порядок реже. Если так, просто держите это в одном редисе вашего проекта. ОЗУ соседнего сервера так же быстрый и надежный по скорости выдачи источник данных.

5. Я бы не обрабатывал повторно то, что не удалось выполнить с первой попытки. Скорее всего, это случится из-за реальной проблемы. Например, файл таблицы побился или спаммер там наклепал 1М строк, SQL зависает, воркер очереди зависает и т.д. А вы будете еще сильнее стараться выполнить то, что зависает. Это приведет к проблеме быстрого зависания всего проекта из-за остановившихся воркеров. Помните совет, как бороться с лавиной?

6. Хранение логов - это отдельный мини проектик. Посмотрите презентации других проектов, как они собирают и обрабатывают их. Это вы как-нибудь и сами придумаете (поиск по ошибкам и все дела). А совет такой, сделать потом мониторинг скорости поступления пхп ошибок. Особенно это важно в момент выкладки нового релиза. Если показатель ошибок в секунду подскочил, нужно откатить версию.

Если проект небольшой, для начала можно использовать эластик. Как будет расти - придется уменьшать глубину хранения ошибок. А потом и как-то распределенно их там хранить.
dr_eburg
Jul. 25th, 2016 08:23 pm (UTC)
)))
Никита Андреевич
Aug. 7th, 2016 09:08 am (UTC)
Nikita Andreevich
1. Я не нашел никаких упоминаний в сети по поводу архитектуры TopFace. Я слышал, что несколько лет назад проходили мероприятия на которых могли ответить на эти вопросы ваши специалисты(кажется это был 2012й). Где сейчас можно найти информацию по этому поводу?


2. Что такое сверхбыстрый доступ к MySQL?

3. Как вы считаете, каким образом хранятся сообщения в соц. сетях vk.com и различных месседжерах типа Viber, Telegram?

4. TopFace использует ли Redis?

5. Правильно ли я понимаю: быстрые данные(посты и сообщения, лайки) сначала какое то время хранятся в оперативной памяти серверов, соединение которых c клиентом поддерживается с помощью пулов ?

6. Я внимательно прочитал Вашу статью, и стало интересно как же все-таки хранить статус "друзья" и 'подписан на вас' двух разных пользователей. Я понимаю что если делать так называемые связи в табличке то это неплохой способ решения проблем. Но если придется масштабировать проект то этот метод идиотский. Если еще хуже метод, это хранить список друзей у каждого пользователя в формате строки JSON. Но опять же мы упираемся в то, что это не масштабируемо. И в то что делать выборки данных(фильтрация) не представляется возможным
spb_borodin
Oct. 2nd, 2016 08:08 pm (UTC)
Re: Nikita Andreevich
1. Услышать все об архитектуре Топфейса, а так же о многих других проектах, можно на мастер-классе. Проводится каждый день. При необходимости, можно заказать его себе в офис.

2. Не знаю, зависит от контекста, где вы это услышали. Здесь много чего скрыто:
а) по иному хранить данные, чтобы при росте база не умерла (доступ не быстрый, а не падающий)
б) использовать sql обычные таблицы как key/value хранилище
в) использовать оптимизации, другие сборки SQL
г) настройки базы и сервера
Но главное, это архитектура данных.

3. Все всегда хранится в MySQL или PgSQL. Считайте их просто интерфейсом к жесткому диску. Но с применением поверх них nosql хранилищ, типа редиса, которые мапят (главное) и кешируют некоторые данные.

4. Да всего его используют.

5. Хм... нет, не совсем. Когда юзер шлет сообщение, оно действительно хранить в ОЗУ. Только этим местом является какая-то очередь для доставки. Спустя несколько мгновений, сообщение записывается в SQL и то места из пункта 3 выше, чтобы сделать быстро его выбирать. Клиенты не важно с кем соединены. Это просто веб-сервер, которому все равно, он ничего не хранит. Есть небольшие исключения в реализации мобильных клиентов, там сервер, держащий соединение, может кое-что хранить.

6. Друзья/подписчики - это просто список ID. Просто список можно просто в SQL поместить. Если речь идет об очень популярном пользователе, то нет проблемы разместить 1М числовых значений. Ну, можно и больше, если у вас по ляму подписчиков, значит у вас уже очень крупный проект, далее сами додумаете (на мастер-классе расказываю, незачем заранее париться о "мелочах"). Единственная важная особенность - таких табличек будет много, на каждый юзерский спот (группу из 1000 юзеров). А не одна общая.

Юрий Мельгош
Oct. 2nd, 2016 07:11 pm (UTC)
Хранение и отдача большого объема mp3 файлов
Здравствуйте Дмитрий, в продолжении недавнего разговора. Подскажите пожалуйста как организовать хранение и быструю отдачу большого объема файлов, это не обязательно могут быть mp3, это могут быть картинки большого размера и.т.д. Собственно вопросы:

1. Как выглядит сама организация хранения файлов в файловой системе? Т.е как хранить большое количество файлов, нельзя же все в один каталог засунуть? Какое на ваш взгляд оптимальное решение, с точки зрения не только хранения как мертвых файлов, но так же и горячей отдаче, ведь файлы большого размера в бесплатную БД не засунуть?

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



spb_borodin
Oct. 2nd, 2016 08:25 pm (UTC)
Re: Хранение и отдача большого объема mp3 файлов
Я уже отвечал в других топиках на этот вопрос. Ну, т.к. конкретики не вижу, отвечаю общими словами.

Файлы хранятся как просто файлы на диске. Но не пробуйте то, что я далее напишу, пока проект не взлетит. Можно хранить файлы не как файлы, а писать напрямую на жесткий диск, в обход операционной системы. Смещение таких файлов на диске хранить в редисе. Решение https://habrahabr.ru/post/184652/ или от яндекса аналогичное.

Если файлы - это видео (большие), то трюки выше не нужны, смысла нет.

Должна быть иерархия папок, разумеется:

/название проекта/номер файлового спота/номер юзера/типа файлов/имя файл (разрешение 200х200px).jpg

Спот - это номер группы пользователей. К примеру, 99999 - номер юзера, 100 - его спот, остальное должно быть понятно:

/project/100/99999/albums/2/2001_origin.jpg, 2001_small.jpg, ...
/project/100/99999/avatar_100x200.jpg

Далее ограничить число фоток в одном альбоме чем-то разумным (10К фоток).

Отдаются файлы с диска просто nginx. Он получает в URL путь к файлу и просто его выдает. Модули типа access проверят "законность" такого доступа для приватных фоток.

-------------------

Когда юзер (99999) закачивает файл, нужно узнать его номер спорта (100). Далее посмотреть в конфиг, который скажет, на каких 2х серверах хранятся медиа данные сотого спота, допустим server2 (100%) & server3 (0%). В скобках - приоритет выдачи файлов, чтобы кеш только на одном сервере память отъедал. Итого, узнали, 2 и 3. Заливаем свежий ютуберский шедевр в спец каталог:

server2:
/project/100/99999/video_for_convert/1.avi

server3:
/project/100/99999/video_for_convert/1.avi

Далее видео конвертится (на отдельном сервере) и на те же два сервера (№2 и №3) раскладывается новый файл:

server2:
/project/100/99999/video/1.avi

server3:
/project/100/99999/video/1.avi

PHP генерит линк на файл БЕЗ указания 2 или 3:
http://100.video.project.com/99999/video/1.avi

Nginx смотрит на "video" и понимает тип ссылки. Далее смотрит на "100" и в своем конфиге (ему заранее сгенериловали его из пхп), видит, соответствие сотки и серверов 2 и 3. Но у двойки имеется приоритет 100%. Если сервер 2 будет жив, nginx (балансер) отдает соединение подчиненному nginx с сервера 2. Тот уже просто выдает файл со своего диска.

Когда server2 сдохнет, трафик пойдет сразу на сервер 3. И у админа будет время и знания (по конфигу), что и где нужно восстановить.

Edited at 2016-10-02 08:26 pm (UTC)
Юрий Мельгош
Oct. 3rd, 2016 12:12 am (UTC)
"Можно хранить файлы не как файлы, а писать напрямую на жесткий диск, в обход операционной системы."

Дмитрий, можете объяснить что это значит? Не совсем понятно "фаилы не как файлы" и в обход операционной системы, пока не понятно.
spb_borodin
Oct. 3rd, 2016 12:15 am (UTC)
1. Я привел ссылку с описанием метода, ключевое слово - O_DIRECT, прямой доступ к диску без файловой системы.
2. Вам этого не надо. Это всего лишь фича. Заниматься нужно архитектурой. Храните файлы на диске как обычно.
Юрий Мельгош
Oct. 5th, 2016 08:10 pm (UTC)
Дмитрий, скажите пожалуйста, а как воркер, который обрабатывает очередь должен запускаться? По крону? или есть какой то другой алгоритм? А что если какой то сервис упал, воркер так и будет по крону стучать в упавший сервис?
spb_borodin
Oct. 5th, 2016 08:51 pm (UTC)
Нужно создать CronManager.php, его раз в минуту запускать из кронтаба (с защитой от параллельного запуска). Этот скрипт работает вечно (завершается раз в сутки) и уже следит, чтобы было запущенно нужно число других пхп-процессов по конфигу.
Юрий Мельгош
Nov. 16th, 2016 06:52 am (UTC)
HDD
Дмитрий, можете посоветовать марку и модель HDD, которые вы используете? Вы же наверняка тестировали диски и выбрали наиболее оптимальный.
spb_borodin
Nov. 16th, 2016 08:27 am (UTC)
Re: HDD
Не тестировали. Хостеры не дают таких опций и ставят то, что сами купят на свое усмотрение. Думаю, разница не значительна. А вот программист, путем изменения логики записи (снижение iops), может добиться огромного результата.
( 17 comments — Leave a comment )